Зробити код внутрішнім, але доступним для тестування одиниць інших проектів


129

Ми ставимо всі наші тестові одиниці у власних проектах. Ми виявляємо, що ми повинні оприлюднити певні класи замість внутрішніх лише для одиничних тестів. Чи все-таки варто уникати цього робити. Які наслідки для пам’яті роблять класи загальнодоступними, а не запечатаними?


Відповіді:


205

Якщо ви використовуєте .NET, атрибут збірки InternalsVisibleTo дозволяє створювати збірки "товаришів". Це конкретні чітко названі збори, яким дозволено отримувати доступ до внутрішніх класів та членів інших зборів.

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

Приклад:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{

23
Я б запропонував поставити #if DEBUG навколо атрибуту, а потім тестування одиниць у налагодженні. Таким чином, ви будете впевнені, що атрибут не встановлений на код випуску.
кухар Стів

Це просто ідея, я не знаю .... Як щодо: #if DEBUG public class IniReader #else внутрішній клас IniReader #endif Напевно, не рекомендується? Чому?
jmelhus

4
Ну навіщо обмежувати тести налагодженнями?
Marco Mp

2
Крім того, лише нітрик, немає необхідності в тому, щоб в зборах "друга" були чітко названі (це може бути хорошою практикою - навіть якщо мій особистий смак говорить інакше, але не є обов'язковим).
Marco Mp

9
Не погоджуюсь. Якщо я будую складний компонент з дуже тонким загальнодоступним API, непрактично і нереально лише тестувати через загальнодоступний API. Ви закінчитеся з незмінним кулькою грязі. Натомість я б ретельно визначив внутрішні блоки та випробував їх окремо. Як нерідко казав Джеремі Д. Міллер: "Перевірте мале, перш ніж випробувати велике".
Денніс Домен

6

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

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

Ідея полягає в тому, що ви хочете лише перевірити поведінку класу, оскільки якщо ви протестуєте деталі внутрішньої реалізації, ваші тести будуть крихкими. Ви повинні мати змогу змінити деталі реалізації будь-якого класу, не порушуючи всіх своїх тестів.

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


2
Деталі щодо впровадження повинні використовуватися як частина обширного тесту. Не заглядайте на приватні змінні ... перевіряйте очікувану поведінку. Якщо тест правильний .. всю внутрішню сантехніку та проводку слід перевірити як частину цього. Голосували.
Gishu

69
я не обов'язково погоджуюся з цим, оскільки ці класи є "загальнодоступними" для інших класів всередині DLL, а функціональність класу повинна бути перевірена незалежно
leora

27
Я також не згоден. Агрегати є одиницями, і вони повинні бути випробувані ізольовано.
Sentinel

5
Чудова загальна настанова, але не будемо догматичною. Тести виконують дві основні цілі: 1) Регресія - переконайтеся, що ви щось не зламали, 2) Підвищення швидкості розвитку. Якщо мені доведеться відстояти величезний сервіс кожен раз, коли мені хочеться написати рядок коду, я буду перешкоджати розвитку. Якщо у мене складний внутрішній твір, я хочу вміти розробляти і перевіряти ізольовано. І навпаки, я не хочу, щоб усе тестувалося ізольовано (таким чином зменшується значення тестування регресії або дублюється тестовий код), але деякий код виправдовує ізоляцію. Можливо, головне - зробити це окремою збіркою.
Лі Йенсен

2
ОП тут правильно. Ви з'єднуєте свої тести з внутрішньою реалізацією. Отже, ваша команда назавжди є рабом, який змінює тести та жахливий глузливий код. Випробовуйте лише загальнодоступні API, будь-які упаковані API lib або API, відкриті в мережі. Весь код слід використовувати через вхідні двері. Впровадження тестування є причиною того, що TDD значною мірою загинув, це просто величезна PITA для перевірки буквально кожного класу всього додатка, а не фокусування на забезпеченні поведінки системи. Я думаю, що майже всі, включаючи "авторитетні" книги, помилялися або не дали зрозуміти це.
Люк Пуплетт

4

для цілей документації

Ви також можете створити внутрішній клас за допомогою Type.GetTypeметоду

приклад

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

для загального типу є різні процеси, як нижче:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});

-5

Заняття можуть бути як публічними, так і закритими.

Але, не робіть цього.

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

Редагувати: я маю на увазі, якщо ви не хочете включати будь-які тестування матеріалів у свою первісну збірку; це також працює, якщо члени приватні.


1
Чекати, що? Ти кажеш, що не робити public sealed class? Які ваші міркування про цей дорогоцінний камінь?
розчавити

@crush traumapony не входив з 2009 року.
Проф.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.