C # "внутрішній" модифікатор доступу під час тестування одиниць


469

Я новачок у тестуванні одиниць, і я намагаюся зрозуміти, чи варто починати використовувати більше "внутрішнього" модифікатора доступу. Я знаю, що якщо ми використовуємо 'Internal' та встановимо змінну збірки 'InternalsVisibleTo', ми можемо перевірити функції, які ми не хочемо оголошувати загальнодоступними з проекту тестування. Це змушує мене думати, що я завжди повинен використовувати "внутрішній", оскільки принаймні кожен проект (повинен?) Має власний тестовий проект. Чи можете ви сказати мені, чому я не повинен цього робити? Коли я повинен використовувати "приватне"?


1
Варто зазначити, що ви часто можете уникнути необхідності тестування внутрішніх методів, використовуючи System.Diagnostics.Debug.Assert()в межах самих методів.
Майк Мариновський

Відповіді:


1195

Внутрішні класи потрібно перевірити, і є атрибут асембі:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Додайте це до файлу інформації про проект, наприклад Properties\AssemblyInfo.cs.


70
Додайте його до тестуваного проекту (наприклад, у Властивості \ AssemblyInfo.cs). "MyTests" буде тестовою збіркою.
EricSchaefer

86
Це справді має бути прийнятою відповіддю. Я не знаю про вас, хлопці, але коли тести "занадто далекі" від коду, який вони тестують, я, як правило, нервую. Я все за те, щоб не тестувати те, що було позначено як private, але занадто багато privateречей цілком може вказувати на internalклас, який намагається витягнути. TDD або немає TDD, я вважаю за краще мати більше тестів, які перевіряють багато коду, ніж мати кілька тестів, які виконують однакову кількість коду. І уникнення тестування internalматеріалів не точно допомагає досягти хорошого співвідношення.
sm

7
Між @DerickBailey та Dan Tao триває велика дискусія щодо семантичної різниці між внутрішнім та приватним та необхідністю тестування внутрішніх компонентів. Ну варто прочитати.
Kris McGinnes

31
Обгортання та #if DEBUG, #endifблок дозволить цю опцію лише у налагодженнях.
Реальний Едвард Каллен

17
Це правильна відповідь. Будь-яка відповідь, яка говорить про те, що тільки загальнодоступні методи повинні бути перевірені одиницею, відсутня точка одиничних тестів і виправдання. Функціональне тестування орієнтоване на чорний ящик. Тестові одиниці орієнтовані на біле поле. Вони повинні перевірити "одиниці" функціональності, а не лише публічні API.
Гуннар

127

Якщо ви хочете перевірити приватні методи, перегляньте PrivateObjectі PrivateTypeв Microsoft.VisualStudio.TestTools.UnitTestingпросторі імен. Вони пропонують прості у використанні обгортки навколо необхідного коду відбиття.

Документи: PrivateType , PrivateObject

Для VS2017 та 2019 ви можете їх знайти, завантаживши нуль MSTest.TestFramework


15
Під час голосування, будь ласка, залиште коментар. Дякую.
Брайан Расмуссен

35
Дурне голосування за цю відповідь. Це вказує на нове рішення та справді гарне, про яке не згадувалося раніше.
Ігнасіо Солер Гарсія

Мабуть, є певна проблема з використанням TestFramework для націлювання на додатки .net2.0 або новіших: github.com/Microsoft/testfx/isissue/366
Johnny Wu

41

Додавши відповідь Еріка, ви також можете налаштувати це у csprojфайлі:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Або якщо у вас є один тестовий проект для кожного тестування, ви можете зробити щось подібне у своєму Directory.Build.propsфайлі:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Дивіться: https://stackoverflow.com/a/49978185/1678053
Приклад: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12


2
Це має бути головна відповідь imo. Всі інші відповіді дуже застаріли, оскільки .net відходить від інформації про збірку та переміщує свою функціональність у визначення csproj.
mBrice1024

12

Продовжуйте використовувати приватне за замовчуванням. Якщо учасник не повинен піддаватися впливу цього типу, він не повинен бути викритий за межами цього типу, навіть в рамках одного проекту. Це зберігає речі безпечніше і акуратніше - коли ви використовуєте об'єкт, то зрозуміліші, які методи ви повинні використовувати.

Сказавши це, я думаю, що розумно іноді робити природно-приватні методи внутрішніми для тестових цілей. Я віддаю перевагу тому, щоб використовувати рефлексію, що є недоброзичливим.

Одне, що слід врахувати, може бути суфіксом "ForTest":

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Тоді, коли ви використовуєте клас в рамках одного проекту, очевидно (зараз і в майбутньому), що ви дійсно не повинні використовувати цей метод - він існує лише для тестових цілей. Це трохи гакітно, і не те, що я роблю сама, але це, принаймні, варто розглянути.


2
Якщо метод є внутрішнім, чи не виключає його використання тестова збірка?
Ральф Шиллінгтон

7
Я періодично використовую ForTestпідхід, але завжди вважаю його мертвим негарним (додавання коду, який не дає фактичної цінності з точки зору логіки виробничого бізнесу). Зазвичай я вважаю, що мені довелося використовувати підхід, тому що дизайн дещо невдалий (тобто потрібно скидати одинакові екземпляри між тестами)
ChrisWue

1
Спокусився спростувати це - в чому різниця між цим хаком і просто роблячи клас внутрішнім замість приватного? Ну хоча б із умовами компіляції. Тоді воно стає справді безладним.
CAD заблокували

6
@CADbloke: Ви маєте на увазі зробити метод внутрішнім, а не приватним? Різниця полягає в тому, що очевидно, що ви дійсно хочете, щоб це було приватним. Будь-який код у вашій виробничій кодовій базі, який викликає метод із ForTest, очевидно, помиляється, тоді як якщо ви просто зробите метод внутрішнім, він виглядає, що це добре використовувати.
Джон Скіт

2
@CADbloke: Ви можете виключити окремі методи в рамках створення випуску так само легко в тому ж файлі, що і за допомогою часткових класів, IMO. І якщо ви це зробите, це говорить про те, що ви не запускаєте свої тести на випуск версії, що для мене звучить як погана ідея.
Джон Скіт

11

Можна використовувати і приватні, і ви можете викликати приватні методи з відображенням. Якщо ви використовуєте програму Visual Studio Team Suite, вона має приємну функціональність, яка створить проксі-сервер для виклику ваших приватних методів. Ось стаття з кодовим проектом, яка демонструє, як ви можете виконати роботу самостійно, щоб випробувати приватні та захищені методи:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

З точки зору того, який модифікатор доступу вам слід використовувати, моє загальне правило починається з приватного та ескалірується у міру необхідності. Таким чином ви розкриєте як мінімум внутрішніх деталей вашого класу, які справді потрібні, і це допоможе зберегти деталі реалізації прихованими, як це має бути.


3

Я використовую Dotnet 3.1.101і .csprojдоповнення, які працювали на мене:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

Сподіваюся, це допомагає комусь там!


1
Нарешті, явно генеруючи інформацію про збірку, це було нарешті змусило мене працювати також. Дякуємо, що опублікували це!
thevioletsaber
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.