Единичное тестирование метода, который вызывает другой метод
Каков наилучший способ unit test метода, который вызывает несколько методов, например:
modify(string value)
{
if(value.Length > 5) replaceit(value);
else changeit(value);
}
Этот псевдо-код имеет метод изменения, который (в настоящее время) вызывает либо replaceit()
, либо changeit()
. Я уже писал тесты для replaceit
и changeit
, поэтому для записи нового теста для модификации будет 99% того же набора кода. Мне нужно проверить это, потому что это может измениться в будущем.
Итак, скопируйте ли существующий тестовый код? Переместить тестовый код на общую функцию? Любые другие идеи? Я не уверен в лучшей практике здесь.
Ответы
Ответ 1
Это классический тестовый сценарий на основе тестирования на основе поведения и поведения.
В этом смехотворно простом примере проверка вывода прекрасна. Однако в какой-то момент вы столкнетесь с проверками, когда проверка состояния после выполнения сложна. Вместо этого вы хотите проверить поведение (например, проверить, что changeit вызывается с определенным значением).
В этот момент вы, вероятно, должны заглянуть в фреймовую инфраструктуру объекта, такую как Rhino.Mocks(.Net) или Mockito (Java), и начать писать код на основе интерфейса.
Ответ 2
Если вы уже тестировали replaceit()
и changeit()
самостоятельно, то единственное, что вы оставили для тестирования, - это условие if. Тест modify()
с несколькими значениями, чтобы убедиться, что он вызывает правильную функцию в правильных условиях (эти условия null
и Strings
длиной 4, 5 и 6 для кода примера, который вы указали).
Ответ 3
У вас есть несколько вариантов. Какой из них лучше всего зависит от деталей, которые не ясны из вашего вопроса.
- test
modify
так же, как если бы это был несвязанный метод. Преимущество: он может в какой-то момент стать одним.
- просто проверьте, что вы получили правильность if-statement. То есть достаточно просто проверить, что тесты заставляют вас написать нужную вам реализацию (где вызов
replaceit
и changeit
- это просто самая простая реализация, которая может работать. Если вы практикуете TDD, это должно прийти к вам естественным образом. Преимущество: высокий охват тестированием без особого дублирования усилий.
- Подкласс и метод переопределения (это метод нарушения зависимости из книги "Эффективная работа с устаревшим кодом" ): протестируйте метод на подклассе, который вы вводите исключительно для целей тестирования, который переопределяет
replaceit
и changeit
с помощью (или переменные, указывающие, был ли метод вызван с правильными значениями). Преимущество: возможно, упростит ваши тесты (или нет), иногда даже просто сделайте тестирование возможным.
- Извлеките новый класс для методов
replaceit
и changeit
, включая интерфейс для этого класса. Stub или Mock этот интерфейс при тестировании modify
. Преимущество: может сделать ваш дизайн более надежным и лучше развязанным/многоразовым в целом (или нет).
Ответ 4
Просто проверьте modify
.
modify
должен возвращать определенные значения при заданных значениях.
Не важно, как модифицирует свою работу - только то, что она выполняет свою работу.
И если в будущем вы измените modify
на использование разных методов (или нет методов), это не будет, а не должно и не повлияет на ваши тесты.
Тем не менее, проверьте replaceit' and
changeit`.
Ответ 5
Что такое "тестовый код" в этом случае? Настройка и проверка результатов? Если это так, я бы реорганизовал его на другой метод и использовал его для каждого теста. Я бы сделал это только в том случае, если у вас есть значительная его часть, - есть читаемость для того, чтобы видеть все, что делает тест, просто прочитав код этого метода.
Сложные методы тестирования часто беспокоят меня, если честно - часто их невозможно избежать, но если вы можете их упростить, это стоит того.
Ответ 6
В порядке предпочтения
- modify (test) имеет всего 2 сценария (каждая рука if stmt), поэтому я бы написал 2 теста для изменения формы.
Если ожидаемый результат замены (значения) легко определить..
.
public TestModifyIfValueLength..()
{
string expectedValue = .. ;// literal result of replaceit(value)
Assert.Equals( expectedValue, modify("asd") );
}
- Если нет, подумайте об использовании заглушки (используйте подкласс и переопределите changeit, replaceit), чтобы убедиться, что был вызван правильный метод.
- Если заглушка - это слишком много работы, делайте макет. Извлеките интерфейс и настройте ожидания на changeit, replaceit.
Допущения
- У вас есть тесты для replaceit (value) и changeit (value), которые всесторонне проверяют (например, все граничные условия) для этих 2 методов.
- replaceit() и changeit() являются общедоступными методами. Если нет, вы должны рассмотреть возможность написания тестов только для общедоступных методов. Вы должны быть свободны в настройке/выкидывании частных методов без знания тестового кода.
Ответ 7
Ну, нет, ваш тестовый код не будет на 99% одинаковым, потому что вы на самом деле тестируете что-то другое здесь, если только замена, изменение и изменение не возвращают одинаковые значения.
Не знаю, почему сложность. Тест для метода модификации должен составлять приблизительно четыре строки. Поскольку вы уже тестируете базовые функции, и все, что вы хотите сделать, это убедиться, что этот конкретный метод не сломается, написав тест, который проверяет два возможных пути кода в этой функции, ожидается, что ожидаемые значения должны быть достаточными.
Ответ 8
Если вы уже написали тесты для replaceit() и changeit(), тест для модификации просто проверит, что разные результаты возвращаются в зависимости от значения 'value'. Однако вы просто будете переопределять логику метода в тесте, что немного абсурдно.
В этом случае я бы не тестировал модификацию до тех пор, пока у нее не будет более сложной логики, или лучше - используется другим методом, который более значим для тестирования.
Ответ 9
При тестировании граничных условий, таких как if (value.length > 5)
, вы должны убедиться, что ваши тестовые данные содержат значения value
, длина которых 4
, 5
или 6
.
Ответ 10
Вы можете создать func из методов и высмеять эти funcs. Или вы можете создавать виртуальные методы и использовать ночные нотки Rhino - частичный макет, вы можете имитировать эти виртуальные методы.
Ответ 11
Вам в основном нужны 2 теста.
1) Переходите в строку типа "Quick Brown Fox Jumps!". (длина больше пяти), убедитесь, что на значение влияет replaceit(...)
2) Передайте строку типа "Foo" (длина меньше пяти) и убедитесь, что на значение влияет changeit(...)
Ваш тест (в псевдокоде) может выглядеть так:
testLongValue() {
string testValue = "A value longer than 5 chars";
string expected = "Replaced!";
string actual = modify(testValue);
assertEqual(expected, actual);
}
testShortValue() {
string testValue = "len4";
string expected = "Changed!";
string actual = modify(testValue);
assertEqual(expected, actual);
}
Очевидно, я мог бы дать вам более реалистичный пример, если бы я знал, что должны были делать функции replacit() и changeit(), но это должно дать вам эту идею. Если он изменяет исходное значение, а не возвращает его, вы можете просто использовать testValue в качестве фактического значения после вызова.
Ответ 12
То же, что и Justin Standard, плюс передача null
в качестве значения (что, очевидно, не удастся для фрагмента кода, который вы нам даете;))
Основное правило для модульного тестирования - "проверить только то, что характерно для тестируемого метода". И это довольно... необычно иметь метод, который не вызывает другого.