Модульное тестирование метода, вызывающего другой метод - PullRequest
50 голосов
/ 12 декабря 2008

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

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

    else changeit(value);
}

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

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

Ответы [ 12 ]

35 голосов
/ 12 декабря 2008

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

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

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

15 голосов
/ 12 декабря 2008

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

  • test modify, как если бы это был не связанный метод. Преимущество: в какой-то момент оно может стать единым целым.
  • просто проверьте, что вы правильно поняли оператор if. То есть просто достаточно проверить, чтобы эти тесты заставили вас написать нужную реализацию (где вызовы replaceit и changeit - это просто простейшая реализация , которая могла бы работать . Если вы практикуете TDD, это должно прийти к вам естественным образом. Преимущество: высокий уровень охвата тестированием без особых дублирующих усилий.
  • Метод подкласса и переопределения (это метод разрушения зависимостей из книги «Эффективная работа с устаревшим кодом»): протестируйте метод на подклассе, который вы вводите исключительно для целей тестирования, который переопределяет replaceit и changeit с постоянными ответами или с тем, чтобы они устанавливали чувствительные переменные (переменные, которые указывают, был ли метод вызван с правильным значением (ями)). Преимущество: может упростить ваши тесты (или нет), иногда даже просто сделать возможным тестирование.
  • Извлечение нового класса для методов replaceit и changeit, включая интерфейс для этого класса. Заглушка или макет этого интерфейса при тестировании modify. Преимущество: может сделать ваш дизайн более тестируемым и лучше отсоединить / повторно использовать в целом (или нет).
15 голосов
/ 12 декабря 2008

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

6 голосов
/ 12 декабря 2008

Просто тест modify.

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

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

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

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

5 голосов
/ 12 января 2009

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

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

.

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

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

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

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

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

2 голосов
/ 24 октября 2012

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

2 голосов
/ 12 декабря 2008

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

2 голосов
/ 12 декабря 2008

Вам в основном нужно 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 декабря 2008

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

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

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