Должна ли каждая ветвь в методе иметь отдельный юнит?

Это скорее вопрос дизайна.

Предположим, что у вас есть такой метод (в качестве примера):

if (x == 5) {
  c = 1;
} else {
 if (z != 2) {
  b = 6;
} else {
   a = 3;
}

Считаете ли вы, что лучше всего иметь junit для каждой возможной ветки? Т.е. testx5, test xnot5znot2, testxnot5z2 и т.д. Или что-то вроде:

void testMethod() {
// x is 5
test/assert code;

// x not 5, z not 2
test/assert code;

// x not 5, z is 2
test/assert code

// etc
}

EDIT: чтобы быть ясным, моя цель - полное покрытие кода. Я просто хочу узнать мнения о том, должен ли я сделать новый тест для каждой ветки или объединить их в одном тесте. Спасибо за ваш вклад.

Ответ 1

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

Ответ 2

То, что вы обсуждаете, называется Branch Coverage.

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

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

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

Ответ 3

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

Теперь, все сказанное, полезно также создавать "краевые случаи", в которых вы тестируете вещи, которые могут сломаться (например, нули, нули, негативы, данные слишком большие/маленькие и т.д.), чтобы увидеть, он также не оправдывает ожиданий. Обычно, чтобы написать их, вам нужно знать, как работает метод. Если вы можете проектировать тесты, которые будут охватывать весь ваш код, это здорово! 100% охват тестирования - это определенная вещь, к которой нужно стремиться, но это не всегда практично (или полезно) в зависимости от ситуации.

Ответ 4

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

Для меня лично это преимущество прекращается, когда вам нужно сделать много скопирования, чтобы настроить/объяснить тестовый файл, в этом случае я просто сделаю несколько утверждений в том же тестовом случае.