Модульное тестирование метода, вызывающего другой метод

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

modify(string value)
{
    if(value.Length > 5)  replaceit(value);

    else changeit(value);
}

Этот псевдо - код имеет ИЗМЕНЯЮТ метод , который ( в настоящее время) вызывает либо replaceit()или changeit(). Я уже написал тесты для replaceitи changeit, поэтому написание нового теста для модификации будет на 99% таким же набором кода. Мне нужно проверить это, подумал, потому что это может измениться в будущем.

Так я могу скопировать вставить существующий тестовый код? Переместить тестовый код в общую функцию? Есть еще идеи? Я не уверен в лучшей практике здесь.

12.12.2008 17:48:46
12 ОТВЕТОВ
РЕШЕНИЕ

Это классический тест на основе состояния, а не сценарий на основе поведения.

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

В этот момент вы, вероятно, должны изучить фальшивую объектную среду, такую ​​как Rhino.Mocks (.Net) или Mockito (Java), и начать писать больше интерфейсного кода.

38
12.12.2008 18:28:22
Вот почему люди продвигают функциональное программирование
Mr.Q 5.01.2020 19:04:52

Если вы уже опробовали replaceit()и changeit()независимо друг от друга, то единственное , что вам осталось тест является , если условие. Тест modify()с несколькими значениями , чтобы убедиться , что он называет правильную функцию при правильных условиях (эти условия , являющиеся nullи Stringsдлиной 4, 5 и 6 для примера кода вы дали).

16
8.08.2016 11:02:15

Ну, нет, ваш тестовый код не будет на 99% одинаковым, потому что вы на самом деле тестируете здесь что-то другое, если только команды replaceit, changeit и modify не возвращают одинаковые значения.

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

2
12.12.2008 17:54:55

Что такое «тестовый код» в этом случае? Настройка и проверка результатов? Если это так, я бы преобразовал его в другой метод и использовал его в каждом из тестов. Я сделал бы это только в том случае, если его было достаточно много - преимущество в удобочитаемости, позволяющее видеть все, что делает тест, просто читая код этого метода.

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

4
12.12.2008 17:56:06
Я не согласен с выделением утверждений из тестов во вспомогательные функции. Я думаю, что тестовый код гораздо удобнее для чтения / сопровождения, когда все утверждения остаются в тестовых функциях. Когда люди выписывают утверждения фактора, они, как правило, начинают «тестирование с помощью дробовика» и перестают думать о своих реальных тестовых примерах.
Scotty Allen 12.12.2008 18:36:03
@ Скотти: Это зависит. Некоторые тесты просто неизбежны для настройки и проверки. Мы можем стараться избегать всего, что нам нравится, но иногда это просто необходимо. Вспомогательные методы могут сохранить много повторяющегося кода.
Jon Skeet 12.12.2008 19:37:04

Если вы уже написали тесты для replaceit () и changeit (), тест на изменение просто проверит, что разные результаты возвращаются в зависимости от значения 'value'. Однако вы будете просто переопределять логику метода в тесте, что немного абсурдно.

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

2
12.12.2008 17:57:40

Просто проверь modify.

Modify должен возвращать определенные значения, когда заданы определенные значения.

Неважно, как модифицирует свою работу - только то, что она делает свою работу.

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

Тем не менее, также проверить replaceit' andchangeit`.

7
12.12.2008 18:07:37

Вам в основном нужно 2 теста.

1) Передайте строку типа «Быстрый скачок коричневой лисы!» (длина больше пяти) гарантирует, что значение зависит от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);
}

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

2
12.12.2008 18:17:30
Вот как бы я это сделал. Поскольку вы полностью протестировали replaceit () и changeit () самостоятельно, вам нужно только протестировать логику, которая содержится в modify (). Тестовый случай, который запускает replaceit (), и тест, который проверяет changeit (), оба проверяют, что они сделали то, что ожидали, должно быть в порядке.
Scotty Allen 12.12.2008 18:33:55
Я бы добавил предельный случай, когда длина ровно 5
Kena 12.12.2008 19:35:33
Я также добавил бы тест для нуля, как напоминание самому себе о добавлении пункта охраны.
Bill the Lizard 12.12.2008 19:55:43

При проверке граничных условий , как if (value.length > 5)вы должны убедиться , что данные теста содержит значение , valueкоторые имеют длину 4, 5или 6.

2
12.12.2008 18:19:43

То же, что и у Джастина Стандарт, плюс передача в nullкачестве значения (что, очевидно, не сработает для фрагмента кода, который вы нам дадите;)) Основное правило для модульного тестирования - «проверять только то, что относится к тестируемому методу». И довольно ... необычно иметь метод, который не вызывает другой.

0
12.12.2008 18:36:55

У вас есть несколько вариантов. Какой из них лучше, зависит от деталей, которые не ясны из вашего вопроса.

  • проверить, modifyкак если бы это был не связанный метод. Преимущество: оно может в какой-то момент стать единым целым.
  • просто проверьте, что вы правильно поняли оператор if. То есть просто достаточно протестировать, чтобы тесты вынудили вас написать нужную реализацию (где вызов replaceitи changeitэто просто простейшая реализация, которая могла бы работать . Если вы практикуете TDD, это должно быть вам естественно. Преимущество: высокий охват тестированием без много дублированных усилий.
  • Подкласс и метод переопределения (это метод разрушения зависимостей из книги «Эффективная работа с унаследованным кодом»). Протестируйте метод на подклассе, который вы вводите исключительно для целей тестирования, который переопределяет replaceitи использует постоянные changeitответы или так, чтобы они устанавливали чувствительные переменные (переменные, которые указывают, был ли метод вызван с правильным значением (ями)). Преимущество: может упростить ваши тесты (или нет), иногда даже просто сделать возможным тестирование.
  • Извлеките новый класс для replaceitи changeitметодов, в том числе интерфейса для этого класса. Заглушка или макет этого интерфейса при тестировании modify. Преимущество: может сделать ваш дизайн более тестируемым и лучше развязанным / повторно используемым (или нет).
17
12.12.2008 19:24:10
+1 Вариант 2 - Вариант 3 - Вариант 4 в таком порядке предпочтения.
Gishu 12.01.2009 05:58:11

В порядке предпочтения

  1. На изменение (тест) просто есть 2 сценария (каждый ответчик команды if stmt), поэтому я бы написал 2 теста для изменения формы.
    Если ожидаемый результат замены (значение) легко определить ..

,

public TestModifyIfValueLength..()
    {
      string expectedValue = .. ;// literal result of replaceit(value)
      Assert.Equals( expectedValue, modify("asd") );
    }
  1. Если нет, подумайте об использовании заглушки (используйте подкласс и переопределите changeit, replaceit), чтобы убедиться, что был вызван правильный метод.
  2. Если заглушка - это слишком много работы, сделайте Mock. Извлеките интерфейс и настройте ожидания на changeit, replaceit.

Предположения

  • У вас есть тесты для replaceit (значение) и changeit (значение), которые всесторонне тестируют (например, все граничные условия для) эти 2 метода.
  • replaceit () и changeit () являются открытыми методами. Если нет, то вам следует рассмотреть возможность написания тестов только для открытых методов. Вы можете свободно настраивать / закрывать закрытые методы без знания тестового кода.
5
12.01.2009 06:14:26

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

2
23.10.2012 22:03:09