Если юнит-тесты охватывают только «функциональное» программное обеспечение

9

Мы используем StructureMap в новом проекте по разработке программного обеспечения. Один из членов команды реализовал модульный тест, который в основном проверяет конфигурацию контейнера StructureMap . Это делается следующим образом;

  • Подсчитывает количество экземпляров сборок, настроенных для классов в нашем пространстве имен приложения.
  • Определяет ожидаемые экземпляры на уровне класса
  • Утверждает, что ожидаемые экземпляры соответствуют общему количеству найденных экземпляров.
  • Утверждает, что ожидаемые экземпляры соответствуют определенным в тесте

Примером этого является;

var repositories = container.GetAllInstances<IEnvironmentRepository>();
Assert.AreEqual(1, repositories .Count());
foundInstances = foundInstances + repositories .Count();

У нас также есть «модульные тесты» для следующего класса;

public MyClass(IEnvironmentRepository environmentRepository)
        {

        }

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

Коллега проигнорировал юнит-тест в конфигурации structmap с комментарием в строке «Юнит-тест только проверяет свою собственную конфигурацию». Это было очевидно целью теста и, на мой взгляд, совершенно справедливо. Я попросил парня, который проигнорировал тест, удалить конфигурацию структуры карты IEnvironmentRepository(тест пока игнорируется) и запустить полный набор модульных тестов, все они прошли. Затем мы запустили приложение, и оно упало, потому что конфигурация контейнера стала недействительной. На мой взгляд, это доказало ценность теста, мой коллега все же не согласился. Он просто заявил, что мы не должны тестировать конфигурацию, но я считаю, что это вполне в рамках юнит-теста.

Итак, ряд вопросов;

  • Является ли это действительным модульным тестом - мы тестируем конфигурацию нашего контейнера, а не то, что Structuremap работает (но я вижу наложение)
  • Если нет, как вы можете проверить конфигурацию, не проверяя ее. Как вы можете остановить кого-то случайно удалить необходимую строку кода и проверить его?
  • Должен ли MyClassмодульный тест разрешить экземпляр IEnvironmentRepositoryиз контейнера и передать его?
ChrisBint
источник
10
9 из 10 разногласий в отношении тестов возникают из-за того, что фреймворки поддерживают автоматические тесты во всех их формах, и люди хотят понять семантику того, является ли конкретный автоматический тест хорошим и надлежащим модульным тестом или нет. Тест, который вы описываете, звучит как вид не совсем юнит-теста-теста, который может быть очень полезным иметь и автоматизировать (и запустить при регистрации) - просто не называйте его юнит-тестом. Спросите, будет ли ваш коллега лучше спать по ночам, если тест находится в отдельной папке / папке, которая была четко разделена.
Йерун Мостерт
2
Это мое мнение, вероятно, полезное, и хотя оно не является строго модульным тестом, оно действительно повышает ценность, и это доказано. Он ответил, что другие модульные тесты подхватили бы это, но, по моему мнению, если бы они были написаны как строгие модульные тесты, вы бы посмеялись над зависимостями и поэтому никогда не знали бы, была ли конфигурация действительной, пока вы ее не использовали.
ChrisBint
4
У вашего коллеги есть пункт, когда он говорит, что не следует тестировать конфигурацию, поскольку подлинная конфигурация, которая на самом деле может варьироваться в зависимости от развертывания, не может / не должна тестироваться - кто сказал, что «красный» - это неправильно, а «синий» - нет? Тест будет тесно связан с одной установкой. Но конфигурация, которая связана с артефактами кода, является исключением из-за того, что она не меняется, и есть очевидные способы ошибиться. В идеале такая конфигурация должна создаваться во время сборки из метаданных DRY, но там, где это невозможно, такой тест добавляет ценность. Лучше, чем избежать ошибки развертывания.
Йерун Мостерт
2
То, что вы описываете, не проверяет модуль, а проверяет конфигурацию стороннего программного обеспечения. Фантастически полезно иметь тесты, которые проверяют эти вещи, но это интеграционные тесты, а не модульные тесты, и причиной разногласий может быть разрыв.
Фоши
3
@ChrisBint Боже мой, нет, я сам написал несколько контейнерных тестов. Они имеют большую ценность, они просто не являются юнит-тестами. Это нормально, интеграционные тесты чрезвычайно полезны для того, чтобы ловить вещи, которые модульные тесты не могут .
Фоши

Ответы:

13

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

Может ли контейнер IoC разрешать и составлять все деревья объектов в приложении? Может ли Auto Mapper сопоставлять все свои зарегистрированные объекты без сбоев? Разве центральный слой в Архитектуре Лука не ссылается на что-то внешнее?

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

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

guillaume31
источник
По иронии судьбы, именно так я объяснил это своему коллеге, и даже с проверкой (удалите один из экземпляров контейнера и запустите приложение) он все еще не видел никакого значения. Я понимаю, что у каждого свое мнение, и я высказал свое;) Мне нравится термин "тест архитектуры", я собираюсь его украсть!
ChrisBint
6

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

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

Кроме того, если вы добавили дополнительное требование зависимости, но забыли добавить его в контейнер и изменить тест контейнера. все пройдет, но ваша программа потерпит крах.

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

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

Сказав это, растущая сложность настройки контейнера является болью в заднице. Возможно, некоторые «плохие» тесты того стоят.

Ewan
источник
1

Модульные тесты, тестовый код. Все, что находится за пределами этого, является «другим» автоматическим тестированием - называйте это как хотите. Вы, кажется, тестируете конфигурацию здесь. Если конфигурация может измениться в зависимости от среды, она не входит в модульный тест. Попробуйте добавить атрибут test, чтобы указать, что этот тест отличается от других тестов.

Робби Ди
источник
Конфигурация является статической, она не управляется средой, все классы, которые существуют в конфигурации, будут использоваться во всех средах одинаково. Да, количество экземпляров, которые могут быть в конфигурации, должно соответствовать количеству экземпляров в конфигурации, которая является частью теста. Как показал мой пример, удаление IEnvironmentRepository позволило пройти остальные модульные тесты. Конкретный контейнерный тест не прошел бы по 2 утверждениям; 1 - общее количество возможных объявлений экземпляров не совпадало, и 2 - не совпадало конкретное количество экземпляров IEnvironmentRepository.
ChrisBint
1
Правильность контейнера определяется кодером. Тот факт, что как тестируемый код, так и сам тест должны быть изменены для каждой модификации, немедленно вызывает сигнал тревоги. DI - это средство для достижения цели, а не самоцель. Вполне возможно написать код в стиле DI без Structuremap, так что, на мой взгляд, это не настоящий юнит-тест. Контейнер конечно должно быть доказано , но эффективность делать это с помощью автоматизированных тестов , казалось бы, несколько спорно с ограниченной информации, представленной здесь.
Робби Ди
2
Юнит тесты заняли 10 минут, чтобы нокаутировать. Развертывание может занять более часа.
ChrisBint
1
Данная часть модульного теста специально проверяет наличие одной строки в конфигурации, не зная, как это не могло быть более изолированным. Общий подсчет, с которым я мог согласиться.
ChrisBint
1
Тогда в них может быть какой-то пробег - судья для вас. Но они должны быть отделены либо физически, либо с помощью какого-либо атрибута.
Робби Ди
0

Обязанность контейнера ввода зависимостей - склеивать разные модули в одном рабочем приложении .

Если вы пишете автоматизированные тесты для своего приложения - у вас должно быть несколько «интеграционных (или приемлемых) тестов, которые выполняют тесты« от конца до конца », которые будут тестировать весь конвейер вашего приложения, чтобы все модули, участвующие в конкретном тестовом примере, были склеены вместе правильно .

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

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

Если тесты интеграции не охватывают создание какой-то конкретной зависимости - вы просто добавляете такую.

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

Fabio
источник
0

ИМО, ответы таковы:

  1. Является ли это действительным модульным тестом - мы тестируем конфигурацию нашего контейнера, а не то, что Structuremap работает (но я вижу наложение)

    • Это действительный модульный тест для структуры карты , а не для вашего проекта, потому что унитарный тест проверяет некоторый конкретный код, при необходимости проверяя все зависимости, чтобы проверить реализованную логику. Логика конфигурации реализована внутри структуры карты, поэтому эта библиотека должна быть хорошо протестирована и должна содержать модульные тесты, подобные тому, который вы упомянули, и многое другое: она должна содержать сотни таких тестов, динамически моделировать несколько конфигов во время выполнения и тестировать, чтобы увидеть, Контейнер ведет себя как следует.
  2. Если нет, как вы можете проверить конфигурацию, не проверяя ее. Как вы можете остановить кого-то случайно удалить необходимую строку кода и проверить его?

    • Вы можете протестировать конфигурацию вручную в необходимой среде, а также вы можете создать автоматизацию для этого (автоматизированный тест), который тестирует для конкретной конфигурации, которая вам нужна (не нужно дразнить вещи во время выполнения).
  3. Должен ли модульный тест MyClass разрешить экземпляр IEnvironmentRepository из контейнера и передать его?

    • Нет, это идеальный модульный тест, потому что вы макетируете зависимость и тестируете логику MyClass изолированным способом.
Эмерсон Кардосо
источник
-1

UnitTest проверяет желаемое поведение подразделения в отделении .

Это означает, что любая конфигурация не входит в объем UnitTests .

Тем не менее у вас должны быть автоматические тесты для ваших конфигураций, но это не UnitTests ...

Тимоти Тракл
источник
Где вы получаете определение единиц?
ChrisBint
Мне нравится Рой Ошеров в «Искусстве юнит-тестирования» : юнит - это любой кусок (производственного) кода, который имеет ту же причину для изменения. В моем мире это обычно колеблется от одного класса до трех или пяти ...
Тимоти Тракл
Это производственный код, который тестируется.
ChrisBint
Просто хотел отвлечь придурков , не ожидая, что это сработает и наоборот ...; o)
Тимоти Тракл