Ви ставите тестові одиниці в тому ж проекті чи іншому проекті?


137

Ви ставите тестові одиниці в одному проекті для зручності чи ви ставите їх в окрему збірку?

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

Відповіді:


100

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

  1. Одиничні випробування постачаються з виробничим кодом. Єдине, що постачається з кодом товару - це виробничий код.
  2. Збори будуть непотрібно роздуті одиничними тестами.
  3. Експериментальні випробування можуть впливати на процеси нарощування, такі як автоматичне або безперервне збирання.

Я не знаю жодних плюсів. Наявність додаткового проекту (або 10) - це не підступ.

Редагувати: Детальніше про збірку та доставку

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

Підводячи підсумок, ось загальна ідея щоденного складання та тестування та доставки бітів та інших файлів:

  1. Виконання збірки виробництва, розміщення виробничих файлів у конкретному "виробничому" каталозі.
    1. Створюйте лише виробничі проекти.
    2. Скопіюйте складені біти та інші файли у каталог "виробництво".
    3. Скопіюйте біти та інші файли в каталог кандидатів до випуску, він також буде "Release20081225".
  2. Якщо нарощування виробництва успішно, працює тестова збірка одиниць.
    1. Скопіюйте виробничий код у каталог "тести".
    2. Побудуйте одиничні тести до каталогу "тести".
    3. Виконати одиничні тести.
  3. Надсилайте розробникам сповіщення про складання та результати тестів на одиниці.
  4. Коли кандидат у випуск (наприклад, Release20081225) приймається, надсилайте ці біти.

15
ІМО, перелічені вами мінуси не завжди застосовні. Професіоналом для цього ж проекту є простіше групування тестованих класів + тестів - ці невеликі зручності проходять довгий шлях при написанні тестів. Тут перемагають особисті переваги, і іноді ваші бали є актуальними, тільки не весь час.
orip

7
Спершу слід запитати, чи потрібно видаляти тести під час доставки. Якщо так, то вам потрібен окремий проект. Якщо ні, то використовуйте інші плюси і мінуси, щоб вирішити. Люди, які припускають, що не можуть розгорнути тести, завжди приймуть висновок "окремого проекту" за замовчуванням.
Оріп

7
Я не бачу жодних переваг щодо доставки невиробничого коду, як-от одиничні тести, і є багато недоліків. Перевірка одиниць доставки означає, що ви маєте справу з більшою кількістю бітів, які потрібно розподілити. Одиничні тести також мають окремий набір залежностей. Тепер ви доставляєте NUnit, Rhino або Moq і т. Д. Це ще більше роздувається. Розміщення тестування одиниць в окремому проекті вимагає лише невеликих зусиль, а це разова вартість. Мені дуже зручно з висновком, що одиничні тести не слід відправляти.
Джейсон Джексон

4
В якості альтернативи підходу "окремого проекту" для видалення одиничних тестів у виробничому коді розглянемо використання користувальницьких символів та директив компілятора на зразок #if. Цей спеціальний символ можна перемикати за допомогою параметрів командного рядка компілятора у ваших програмних скриптах CI. Див. Msdn.microsoft.com/en-us/library/4y6tbswk.aspx .
Rich C

23
.NET знаходиться за кривою наявності тестів у абсолютно окремому проекті, і я б покладався на думку, що це незабаром зміниться. Немає жодної причини, щоб процес збирання не міг бути ігнорований тестовим кодом у версії версії. Я використав кілька генераторів веб-додатків, які роблять це. Я думаю, що абсолютно краще включити тестові кодові файли одиниці поряд з кодовими файлами, які вони описують, що з'являються в тій же ієрархії файлів. Google рекомендує це, зване організацією файлів "фрактал". Чому тести відрізняються від вбудованих коментарів та документації для читання? Компілятор із задоволенням ігнорує останнє.
Брендон Арнольд

112

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

Причини окремих проектів вказані іншими. Зауважте, що якщо ви використовуєте тести, керовані даними, у вас може виникнути досить значна кількість, якщо ви включите тести у виробничу збірку.

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


+1: Я щойно стикався з тестовими одиницями в тому самому проекті, що і основний код, і важко знайти тести серед реального коду - навіть незважаючи на те, що дотримується конвенції про іменування.
Фентон

32
Для менш досвідченого читача це означає, що ви додасте [assembly:InternalsVisibleTo("UnitTestProjectName")]до AssemblyInfo.csфайлу проекту .
Маргус

Дуже корисне доповнення Маргус. Дякую Джону за те, що згадав InternalsVisibleTo у цьому контексті
Bjørn Otto Vasbotten

1
@jropella: Якщо ви підписуєте збірку prod, ви все одно можете зробити внутрішні видимими лише підписані збори. І звичайно, якщо ви користуєтесь будь-яким кодом з повним довірою, вони мають доступ через відображення ...
Джон Скіт

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

70

Я не розумію частого заперечення проти розгортання тестів з виробничим кодом. Я очолив команду на невеликій мікрокапці (виріс з 14 до 130 осіб). У нас було півдесятка або близько додатків Java, і ми виявили, що НАЙКРАЙНО цінно для розгортання тестів у полі для їх виконання на певномумашина, яка проявляла незвичну поведінку. Випадкові проблеми трапляються на місцях, і можливість викинути кілька тисяч одиниць тестів при загадці з нульовою вартістю було неоціненним і часто діагностували проблеми за лічені хвилини ..., включаючи проблеми з установкою, невмілі проблеми з ОЗУ, специфічні для машини проблеми, несправні проблеми з мережею, і т. д. і т. д. Я вважаю, що надзвичайно цінним є тести на місцях. Крім того, випадкові проблеми спливають у випадкові часи, і приємно, щоб одиничні тести сиділи там, вже чекаючи їх виконання на мить. Місце на жорсткому диску дешево. Подібно до того, як ми намагаємось зберігати дані та функції разом (OO design), я думаю, що у зберіганні коду та тестів є щось принципово цінне (функція + тести, які підтверджують функції).

Я хотів би поставити свої тести в той же проект у C # /. NET / Visual Studio 2008, але я все ще не досліджував цього, щоб досягти цього.

Одним з головних переваг збереження Foo.cs у тому ж проекті, що і FooTest.cs, є те, що розробникам постійно нагадують, коли в класі відсутній тест для рідних братів! Це заохочує кращі практики тестування на основі тестування ... отвори більш очевидні.


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

+1 Я згоден Як це я роблю здебільшого. NET, тестування доставки з виробничим кодом також має хороший побічний ефект від зменшення безперервного кроку інтеграції компіляції та тесту: 1) лише половина проектів для створення, 2) всі тестові збірки замість основної програми тому простіше параметризувати тестового бігуна. При використанні Maven цей аргумент, звичайно, не дотримується. Нарешті, мої замовники - більш технічні люди, і їм дуже подобається мати можливість самостійно запускати тести, оскільки це документує поточний стан проти специфікації.
mkoertgen

Я думаю, що це може мати ідеальний сенс робити тестування інтеграції у стилі TDD / BDD, тобто написання тестового коду, який легко автоматизувати за допомогою стандартних тестових бігунів, таких як NUnit, xUnit тощо. Звичайно, не слід змішувати блок з тестуванням інтеграції. Просто переконайтеся, що ви знаєте, який набір тестів потрібно швидко та часто виконувати для перевірки збірки (BVT) та які для тестування інтеграції.
mkoertgen

1
Причина, чому люди заперечують проти, - це не те, до чого вони звикли. Якби їх першим тестуванням одиниці досвіду було те, що вони знаходились у виробничому коді, насправді із специфічним синтаксисом на мові програмування, який вказував, що тест пов’язаний із виробничим методом, вони б поклялися, що це неоціненний аспект робочого процесу розвитку і абсолютно божевільно розміщувати їх в окремому проекті. Більшість чортів подібні. Послідовники.
zumalifeguard

19

Для кращої інкапсуляції поставте тестові модулі в той же проект, що і код.

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

Також дуже приємно, щоб одиничні тести були близькими до коду, який ви пишете. Коли ви пишете метод, ви можете легко знайти відповідні тестові одиниці, оскільки це в одному проекті. Коли ви будуєте збірку, що включає unitTests, будь-які помилки в unitTest дадуть вам помилку компілятора, тому ви повинні постійно оновлювати свій тест, просто для створення. Маючи тест на одиничні проекти в окремому проекті, це може змусити деяких розробників забути побудувати проект unittest і пропустити пробиті тести на деякий час.

А ви можете вилучити одиничні тести з виробничого коду, використовуючи компіляційні теги (ЯКЩО #Debug).

Тести автоматичної інтеграції (зроблені в NUnit) повинні бути окремим проектом, оскільки вони не належать ні до одного проекту.


1
Але це означає, що ви не можете запускати тести на Releaseзбірку, і мало хто може «пропустити».
hIpPy

3
За допомогою друзів ( msdn.microsoft.com/en-us/library/0tke9fxk.aspx ) використання InternalsVisibleToдозволяє перевірити внутрішні методи окремого тестового проекту
ParoX

3
@hlpPy - ви можете налаштувати довільні умовні символи компіляції, і ви можете мати більше двох конфігурацій побудови. Наприклад; ви можете мати Debug, Approvalі Release; і скласти затвердження з усіма оптимізаціями випуску. Крім того, як правило, одиничні тести не чудово виявляють в першу чергу heisenbugs (на мій досвід). Вам, як правило, потрібні специфічні регресійні тести інтеграції для них - і ви цілком можете розмістити їх поруч.
Еймон Нербонна

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

12

Провівши деякий час у проектах TypeScript, де тести часто розміщуються у файлі поряд із кодом, який вони тестують, я вирішив віддавати перевагу такому підходу, ніж зберігати їх окремо:

  • Швидше перейти до тестового файлу.
  • Легше запам'ятати перейменування тестів, коли ви перейменовуєте тестований клас.
  • Легше запам'ятати переміщення тестів, коли ви переміщуєте клас, який тестується.
  • Це відразу очевидно, якщо в класі відсутні тести.
  • Вам не потрібно керувати двома дублікатними файловими структурами, одну для тестів та одну для коду.

Тож коли я нещодавно розпочав новий проект .NET Core, я хотів побачити, чи можна імітувати цю структуру в проекті C # без доставки тестів чи тестових збірок з остаточним випуском.

Подання наступних рядків у файл проекту, здається, працює добре зараз:

  <ItemGroup Condition="'$(Configuration)' == 'Release'">
    <Compile Remove="**\*.Tests.cs" />
  </ItemGroup>
  <ItemGroup Condition="'$(Configuration)' != 'Release'">
    <PackageReference Include="nunit" Version="3.11.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
  </ItemGroup>

Вищезазначене гарантує, що в Releaseконфігурації всі названі файли *.Tests.csвиключаються з компіляції, а також видаляються необхідні посилання пакета тестування одиниць.

Якщо ви все ще хочете мати можливість тестувати класи в конфігурації їх випуску, ви можете просто створити нову конфігурацію, похідну від Releaseчогось подібного ReleaseContainingTests.


Оновлення: Після деякого використання цієї техніки я також виявив, що корисно налаштувати ваші іконки у коді VS, щоб тести (та інші речі) виділялися трохи більше на панелі провідника:

Скріншот коду VS

Для цього скористайтеся розширенням Тема Значка Матеріал та додайте щось подібне до Ваших налаштувань коду VS JSON:

"material-icon-theme.files.associations": {
  "*.Tests.cs": "test-jsx",
  "*.Mocks.cs": "merlin",
  "*.Interface.cs": "yaml",
}

Саме це ми тільки почали робити
Метью Стіплз

Ви також працюєте для бібліотек стандартного класу .net?
Zenuka

10

Мої одиничні тести завжди проходять в окремому проекті. Насправді для кожного проекту, який я маю в своєму рішенні, є окремий тестовий проект, який йде разом з ним. Код тестування не є кодом програми, і його не слід змішувати. Однією з переваг збереження їх в окремих проектах - принаймні за допомогою TestDriven.Net - є те, що я можу правою кнопкою миші натиснути на тестовий проект і запустити всі тести в цьому проекті, протестуючи всю бібліотеку коду програми одним клацанням миші.


1
Тоді як ви підрозділяєте тестові внутрішні класи? Або ви лише тестуєте громадські заняття?
user19371

7
<збірка: InternalsVisibleTo = "TestProject" /> при необхідності, хоча я, як правило, перевіряю лише загальнодоступні інтерфейси.
tvanfosson

@tvanfosson, що ти робиш із Specs (Specflow)? У вас був би окремий проект чи ви включили їх у проект Unit Test? +1.
w0051977

@ w0051977 Я ніколи не використовував Specflow, тому не знаю
tvanfosson

@tvanfosson, що з тестами на інтеграцію? Чи помітили б ви їх в окремий проект до одиничних тестів?
w0051977

10

Якщо використовується NUnit фреймворк, є додаткова причина для тестування в одному проекті. Розглянемо наступний приклад виробничого коду, змішаного з одиничними тестами:

public static class Ext
{
     [TestCase(1.1, Result = 1)]
     [TestCase(0.9, Result = 1)]
     public static int ToRoundedInt(this double d)
     {
         return (int) Math.Round(d);
     }
}

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

Оновлення : Я знаю, що таке використання TestCaseатрибуту було не те, що розробники NUnit мали намір, але чому б ні?


2
Мені подобається ця ідея; маючи кілька прикладів входів та очікуваних виходів прямо з кодом.
Престон МакКормік

3

Я коливаюсь між одним і тим же проектом та різними проектами.

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

Під час тестування в одному проекті мені стає легше перемикатися між тестами та кодом, який вони перевіряють, і простіше переробляти / переміщувати їх.


3

Я розміщував їх в окремих проектах. Назва збірки відображає назву просторів імен, як загальне для нас правило. Отже, якщо є проект під назвою Company.Product.Feature.sln, він має вихід (назва збірки) Company.Product.Feature.dll. Тестовий проект - Company.Product.Feature.Tests.sln, поступаючись Company.Product.Feature.Tests.dll.

Найкраще зберігати їх в єдиному рішенні та контролювати висновок за допомогою Менеджера конфігурацій. У нас є названа конфігурація для кожної з основних галузей (Розвиток, Інтеграція, Виробництво) замість використання налагодження та випуску за замовчуванням. Після налаштування конфігурацій ви можете їх включити або виключити, натиснувши прапорець "Скласти" в Менеджері конфігурацій. (Щоб отримати Менеджер конфігурацій, клацніть правою кнопкою миші рішення та перейдіть до Менеджера конфігурацій.) Зауважте, що я знаходжу CM в Visual Studio часом помилковим. Пару разів мені довелося зайти в проект та / або файли рішення, щоб очистити цілі, які він створив.

Крім того, якщо ви використовуєте Team Build (і я впевнений, що інші інструменти збирання .NET такі самі), ви можете пов’язати збірку з іменованою конфігурацією. Це означає, що, якщо ви, наприклад, не будуєте тести на одиницю для своєї збірки "Виробництво", проект збірки може також знати про це налаштування, а не будувати їх, оскільки вони були позначені як такі.

Крім того, ми раніше робили краплі XCopy зі збірної машини. Сценарій просто пропускає копіювання будь-якого імені * .Tests.Dll від розгортання. Це було просто, але працювало.


1

Я б сказав, тримайте їх окремо.

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


Це не залежить від технології, яка використовується для покриття тестів? Я маю на увазі, OpenCover вважає покриті рядки коду, якщо вони виконуються тестом, включаючи рядки самого тесту, так що якщо у тестів є несподівані винятки, вони також позначені як непокриті.
Ісаак Льопис

0

Мене дуже надихає блок тестування одиниць бібліотеки Flood NN Роберта Лопеса. Він використовує різний проект для кожного випробуваного класу одиниць і має одне рішення, що містить усі ці проекти, а також основний проект, який збирає та виконує всі тести.

Акуратною річчю є також макет проекту. Вихідні файли знаходяться в папці, але потім папка для проекту VS знаходиться нижче. Це дозволяє робити різні папки для різних компіляторів. Всі проекти VS поставляються з кодом, тому будь-хто дуже легко може виконати будь-який або всі тести одиниці.


0

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

Чому?

По-перше, я дуже прагну підтримувати структуру папки головного проекту однаковою з тестовим проектом. Отже, якщо у мене є файл, Providers > DataProvider > SqlDataProvider.csтоді я створюю таку ж структуру в своїх тестових проектах одиниць, якProviders > DataProvider > SqlDataProvider.Tests.cs

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

По-друге, не завжди дуже просто перейти від класу, який потрібно перевірити, до одиничного тестового класу. Це ще складніше для JavaScript та Python.

Нещодавно я почав практикувати, що кожен створений вами файл (наприклад SqlDataProvider.cs) я створюю ще один файл із суфіксом Test, наприкладSqlDataProvider.Tests.cs

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

Ви навіть можете написати бізнес-правила, щоб просканувати проект та визначити клас, у якому немає файлу .Tests, та повідомити про це власнику. Крім того, ви можете сказати своєму тестовому бігуну легко орієнтуватися на .Testsкласи.

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

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


Я не погоджуюсь. Набагато більш організовано групувати предмети, ніж тестувати окремі заняття. Якщо ви тестуєте своїх постачальників; У вас був би IProvider, ISqlProvider та MockSqlConnection. У папці провайдера ви мали б модульний тест для кожного постачальника. Ви можете піти так глибоко на одиничні тести, але тоді ваші просто тестові написання, а не код, і навіть можуть видалити профі та починає писати код для тестування.
Нік Тернер

0

Як відповіли інші - поставте тести в окремі проекти

Одне, що не було згадано, - це той факт, що ви насправді не можете працювати nunit3-console.exeпроти нічого, крім .dllфайлів.

Якщо ви плануєте запускати свої тести через TeamCity, це створить проблему.

Скажімо, у вас є проект консольного додатка. Коли ви компілюєте, він повертає виконуваний файл .exeу binпапку.

З цього nunit3-console.exeви не зможете запустити жодних тестів, визначених у цій консольній програмі.

Іншими словами, консольна програма повертає exeфайл, а бібліотека класів повертає dllфайли.

Я щойно покусав цим сьогодні, і це смокче :(


0

Я нещодавно розгортався в докер. Я не бачу значення в тому, щоб мати окремий проект, коли Dockerfile може легко скопіювати каталог / src та залишити каталог / test. Але я новачок у dotnet і, можливо, щось не вистачає.


-2

Окремі проекти, хоча я дискутую сам із собою, чи повинні вони поділяти один і той же svn. На даний момент я даю їм окремі сховища svn, одне називається

"MyProject" - для самого проекту

і один подзвонив

"MyProjectTests" - для тестів, пов'язаних з MyProject.

Це досить чисто і має таку перевагу, яка бере на себе проект та зобов’язує тести зовсім окремо. Це також означає, що ви можете здати svn проекту, якщо це потрібно, без необхідності звільняти свої тести. Це також означає, що ви можете мати гілки / магістралі / теги каталогів для своїх тестів та для свого проекту.

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

MyProject
| \ Магістраль
| | \ Код
| \ Тести
| \ Теги
| | \ 0,1
| | | \ Код
| | \ Тести
| \ 0,2
| | \ Код
| \ Тести
\ Гілки
  \ MyFork
   | \ Код
    \ Тест

Мені було б цікаво дізнатися, що інші люди думають про це рішення.


5
Вам не потрібно мати окремих комісій для тестування та коду програми. Вони повинні йти рука об руку, а комісії включатимуть і те, і інше, якщо використовувати дизайн стилю * DD.
eddiegroves
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.