Вы ставите юнит-тесты в том же проекте или другом проекте?

Вы помещаете модульные тесты в один и тот же проект для удобства или в отдельную сборку?

Если вы поместите их в отдельную сборку, как мы, в результате мы получим несколько дополнительных проектов. Он отлично подходит для модульного тестирования при кодировании, но как выпустить приложение без всех этих дополнительных сборок?

17 ответов

Решение

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

  1. Модульные тесты поставляются с производственным кодом. Единственное, что поставляется с кодом продукта - это производственный код.
  2. Сборки будут излишне раздуты модульными тестами.
  3. Модульные тесты могут влиять на процессы сборки, такие как автоматическая или непрерывная сборка.

Я действительно не знаю никаких плюсов. Наличие дополнительного проекта (или 10) не является мошенничеством.

Изменить: Подробнее о сборке и доставке

Кроме того, я бы порекомендовал, чтобы любой автоматизированный процесс сборки размещал производство и модульные тесты в разных местах. В идеале процесс сборки модульного теста запускается только при сборке производственного кода и копировании файлов продукта в каталог модульных тестов. Выполнение этого способа приводит к тому, что фактические биты отделяются для доставки и т. Д. Кроме того, на данном этапе довольно просто запустить автоматическое модульное тестирование для всех тестов в определенном каталоге.

Подводя итог, вот общая идея для ежедневной сборки и тестирования и доставки битов и других файлов:

  1. Производственная сборка запускается, помещая производственные файлы в определенный "производственный" каталог.
    1. Строить только производственные проекты.
    2. Скопируйте скомпилированные биты и другие файлы в "производственный" каталог.
    3. Скопируйте биты и другие файлы в каталог-кандидат на выпуск, иначе каталог рождественских релизов будет "Release20081225".
  2. Если сборка завершается успешно, выполняется сборка модульного теста.
    1. Скопируйте производственный код в каталог "tests".
    2. Сборка юнит-тестов в каталоге "tests".
    3. Запустите юнит-тесты.
  3. Отправлять уведомления о сборке и результаты модульных тестов разработчикам.
  4. Когда кандидат на выпуск (например, Release20081225) принят, отправьте эти биты.

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

Причины для отдельных проектов, как указано другими. Обратите внимание, что если вы используете тесты, управляемые данными, вы можете столкнуться с довольно значительным раздуванием, если вы включите тесты в рабочую сборку.

Если вам нужен доступ к внутренним элементам производственного кода, используйте InternalsVisibleTo.

Я не понимаю частых возражений против развертывания тестов с рабочим кодом. Я руководил командой на небольшом микрокапсе (вырос с 14 до 130 человек). У нас было около полудюжины или около того Java-приложений, и мы обнаружили, что КРАЙНЕ ЦЕННО развертывать тесты в полевых условиях для их выполнения на определенной машине, которая демонстрирует необычное поведение. Случайные проблемы возникают в полевых условиях, и возможность провести несколько тысяч юнит-тестов в тайне с нулевой стоимостью была неоценима и часто выявляла проблемы за считанные минуты... в том числе проблемы с установкой, проблемы с оперативной памятью, проблемы с конкретными машинами, проблемы с сетью, и т.п. Кроме того, случайные проблемы возникают в случайное время, и было бы хорошо, если бы модульные тесты уже находились там, ожидая их выполнения в любой момент. Место на жестком диске дешево. Точно так же, как мы пытаемся объединить данные и функции (разработка ОО), я думаю, что есть нечто фундаментально ценное в том, чтобы хранить код и тесты вместе (функция + тесты, которые проверяют функции).

Я хотел бы поместить свои тесты в тот же проект в C#/.NET/Visual Studio 2008, но я до сих пор не исследовал это достаточно для его достижения.

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

Проведя некоторое время в проектах 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 Code, чтобы тесты (и другие вещи) немного выделялись на панели проводника:

Для этого используйте расширение Material Icon Theme и добавьте в настройки JSON VS Code что-то вроде следующего:

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

Поместите модульные тесты в тот же проект, что и код, чтобы добиться лучшей инкапсуляции.

Вы можете легко протестировать внутренние методы, что означает, что вы не будете делать методы публичными, которые должны были быть внутренними.

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

И вы можете удалить модульные тесты из производственного кода, используя теги компиляции (IF #Debug).

Автоматические интеграционные тесты (made i NUnit) должны быть в отдельном проекте, поскольку они не принадлежат ни одному из проектов.

Мои юнит-тесты всегда идут в отдельном проекте. Фактически, для каждого проекта, который я имею в своем решении, есть отдельный тестовый проект, который сопровождает его. Тестовый код не является кодом приложения и не должен смешиваться с ним. Одно из преимуществ сохранения их в отдельных проектах - по крайней мере, с помощью TestDriven.Net - заключается в том, что я могу щелкнуть правой кнопкой мыши на тестовом проекте и запустить все тесты в этом проекте, тестируя всю библиотеку кода приложения одним щелчком мыши.

Если используется инфраструктура 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, но почему бы и нет?

Я знаю, что это очень старый вопрос, но я бы хотел добавить свой опыт: недавно я изменил привычку модульного тестирования с отдельных проектов на один и тот же.

Зачем?

Во-первых, я очень склонен сохранять структуру папок основного проекта на одном уровне с тестовым проектом. Итак, если у меня есть файл под Providers > DataProvider > SqlDataProvider.cs Затем я создаю ту же структуру в моих проектах модульного тестирования, как Providers > DataProvider > SqlDataProvider.Tests.cs

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

Во-вторых, не всегда очень легко перейти от класса к тестируемому классу. Это еще сложнее для JavaScript и Python.

Недавно я начал практиковать это, каждый созданный файл (например, SqlDataProvider.cs) Я создаю другой файл с суффиксом теста, например SqlDataProvider.Tests.cs

Вначале кажется, что он будет раздутым файлами и ссылками на библиотеки, но в долгосрочной перспективе вы устраните синдром движущихся файлов на первый взгляд, а также убедитесь, что каждый отдельный файл, который является кандидатом на тестирование, будет иметь парный файл с .Tests суффикс. Это позволяет легко перейти в тестовый файл (потому что он находится рядом) вместо просмотра отдельного проекта.

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

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

Я использую эту практику некоторое время, и я думаю, что это очень разумный компромисс между размером проекта и ремонтопригодностью и кривой обучения для новичков в проекте.

Я колеблюсь между тем же проектом и разными проектами.

Если вы выпускаете библиотеку, выпуск тестового кода вместе с рабочим кодом является проблемой, в противном случае я нахожу, что обычно это не так (хотя перед попыткой существует сильный психологический барьер).

Когда я помещаю тесты в один и тот же проект, мне легче переключаться между тестами и кодом, который они тестируют, и легче реорганизовывать / перемещать их.

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

Лучше всего хранить их в одном решении и контролировать вывод через Configuration Manager. У нас есть именованная конфигурация для каждой из основных ветвей (разработка, интеграция, производство) вместо использования отладки и выпуска по умолчанию. После того как вы настроили свои конфигурации, вы можете включить или исключить их, установив флажок "Сборка" в Configuration Manager. (Чтобы получить Configuration Manager, щелкните правой кнопкой мыши решение и перейдите в Configuration Manager.) Обратите внимание, что иногда я обнаруживаю, что CM в Visual Studio содержит ошибки. Пару раз мне приходилось заходить в файлы проекта и / или решения, чтобы очистить цели, которые он создал.

Кроме того, если вы используете Team Build (и я уверен, что другие инструменты сборки.NET такие же), вы можете связать сборку с именованной конфигурацией. Это означает, что если вы, например, не собираете свои модульные тесты для вашей сборки "Production", проект сборки может знать об этом параметре и не собирать их, поскольку они были помечены как таковые.

Кроме того, мы использовали XCopy для сборки машины. Скрипт просто исключает копирование всего, что называется *.Tests.Dll, из развернутого. Это было просто, но сработало.

Я бы сказал, держите их отдельно.

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

Я действительно вдохновлен структурой модульного тестирования библиотеки Flood NN Роберта Лопеса. Он использует разные проекты для каждого отдельного тестируемого модуля и имеет одно решение, содержащее все эти проекты, а также основной проект, который компилирует и запускает все тесты.

Аккуратная вещь - это также макет проекта. Исходные файлы находятся в папке, но папка для проекта VS находится ниже. Это позволяет создавать разные подпапки для разных компиляторов. Все проекты VS поставляются с кодом, поэтому любой может легко выполнить любой или все модульные тесты.

Недавно я разворачивался в докере. Я не вижу смысла в наличии отдельного проекта, когда Dockerfile может легко скопировать каталог /src и покинуть каталог /test. Но я новичок в dotnet и, возможно, что-то упускаю.

Как ответили другие - ставьте тесты в отдельные проекты

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

Если вы планируете проводить тесты через TeamCity, это создаст проблему.

Допустим, у вас есть проект консольного приложения. Когда вы компилируете, он возвращает исполняемый файл.exe к bin папка.

Из nunit3-console.exe вы не сможете запускать какие-либо тесты, определенные в этом консольном приложении.

Другими словами, консольное приложение возвращает exe файл и библиотека классов возвращает dll файлы.

Меня это только что укусило сегодня, и это отстой:(

Еще одним недостатком отправки кода модульного тестирования в производство является то, что мы отправляем знания о том, как на самом деле использовать этот код. Это может быть плохо, потому что злоумышленнику будет легче понять бизнес-логику после успешной декомпиляции модульных тестов. Это может быть действительно вредно. Например, ваш код может содержать специальную логику для учета особенностей налогообложения между штатами и странами. Это знание будет легко украдено злоумышленником, у которого будет возможность продать его другим игрокам в той же области... просто скопировав код в процессе производства. По моему опыту, почти ни один код в .NET не обфусцирован правильно в продакшене. Есть много инструментов, которые легко декомпилируют код в очень доходчивый вид.

Отдельные проекты, хотя я спорю с собой, должны ли они делиться одним и тем же svn. На данный момент я даю им отдельные репозитории SVN, один называется

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

и один называется

"MyProjectTests" - для тестов, связанных с MyProject.

Это довольно чисто и имеет то преимущество, что коммиты в проект и коммиты на тесты совершенно разные. Это также означает, что при необходимости вы можете передать svn проекта, не выпуская тесты. Это также означает, что вы можете иметь каталоги branch/trunk/tag для ваших тестов и для вашего проекта.

Но я все больше склоняюсь к тому, чтобы в каждом проекте было что-то вроде следующего в одном хранилище SVN.

Мой проект
| \ Магистральные
| |\Code
|  \ тесты
| \ Теги
| |\0,1
| | |\Code
| |  \ тесты
| \ 0,2
|   |\Code
|    \ тесты
\Ветви
  \MyFork
   |\Code
    \Тестовое задание

Мне было бы интересно узнать, что другие люди думают об этом решении.

Вы помещаете модульные тесты в один и тот же проект для удобства или помещаете их в отдельную сборку?

Нет, никогда, никогда не делай этого. Потребители вашего компонента не могут отделить ваш тестовый код от вашего производственного кода и, следовательно, они не могут использовать его для чего-либо нетривиального.

С точки зрения потребителя, заботящегося о безопасности, каждый общедоступный символ в вашей сборке является частью области поверхности для атаки продуктов вашего клиента. Зачем им использовать сборку, которая включает в себя любую непроизводственную зону безопасности?

ПОЗОР ВАМ, ЕСЛИ ВЫ ДУМАЕТЕ, ЧТО НУЖНО ВКЛЮЧАТЬ ИСПЫТАНИЯ УСТАНОВКИ В ПРОИЗВОДСТВЕННЫЕ СБОРКИ!

И позор вам, если вы принимаете такие сборки для включения в свои продукты.


Можно ли публиковать тестовые сборки? Конечно, это так, и я считаю это лучшей практикой. Они могут быть большим подспорьем для поддержки клиентов, и ваши клиенты могут также захотеть включить их в свои собственные тестовые среды. Только не навязывайте клиентам свои тестовые задания.

Другие вопросы по тегам