Должен ли каждый модульный тест выполняться независимо от других тестов?

24

Скажем, у вас есть тесты для двух методов класса. Первый метод собирает данные из другого уровня и помещает их в какое-то хранилище независимо от времени выполнения (например, таблицы SQL), поэтому все данные, обрабатываемые этим тестом, жестко запрограммированы в тесте. Второй метод отвечает за получение данных от того места, где их оставил первый метод, и их преобразование каким-либо образом (расчет, перемещение определенных частей в другое место и т. Д.).

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

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

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

Какой вариант лучше? Есть ли какая-то альтернатива, например, наличие одного теста для каждого изолированного метода с жестким кодированием, а затем более крупные тесты, содержащие оба метода в одном?

Морган Херлокер
источник
2
Мне бы очень хотелось, чтобы я мог легко рандомизировать порядок юнит-тестов при каждом запуске. Сейчас они работают в неизвестном, хотя и относительно фиксированном порядке.
Работа

Ответы:

11

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

Если ваши методы действительно независимы, это не должно иметь значения. Ваш второй метод должен:

а) Работать правильно, когда представлены действительные данные.

б) Сбой разумно и последовательно, когда представлены с неверными данными.

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

Если вы хотите проверить, что методы работают вместе, то это интеграционное тестирование, а не модульное тестирование.

ChrisF
источник
27

Если тесты не могут выполняться независимо, то они не являются юнит-тестами.

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

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

Стив
источник
@ Стив, так что в этом примере, вы бы сказали: один тест для метода 1, один тест для метода 2 и один тест, который запускает 1 и 2 в одном тесте?
Морган Херлокер
2
Да. первые два будут модульными тестами, третий - интеграционным тестом.
Стив
если у вас есть модуль клиента и модуль заказов, то заказ не может быть создан без отношения к клиенту. Как вы будете тестировать его независимо от модуля клиента: создайте запись клиента в базе данных с помощью sql (вставьте в клиента) или используйте Customer.createCustomer (). И ИМХО лучше использовать второй вариант, так как вам не нужно реализовывать какую-либо логику в тесте, но он работает только в том случае, если ваш тест по созданию клиентов пройдет успешно.
Дайний
@Dainius. В сценарии модульного тестирования вы обычно используете фиктивные объекты, поэтому вы передаете фиктивного покупателя в свой модуль заказа. Вы правы в том, что вы не хотели бы использовать sql в этом случае.
Стив
кажется, что в любом сценарии, где метод B зависит от метода A, почти всегда будет метод C, который вызывает A, а затем вызывает B. Поскольку это так, вы можете проверить A, B и C независимо.
Морган Херлокер
9

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

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

Стив Рукутс
источник
1
+1 Раскольников, я не учел тот факт, что позже это будет огромной потерей времени, когда я «проведу все тесты» в дальнейшем.
Морган Херлокер
3

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

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

TrueDub
источник