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.