Модульное тестирование нескольких разных результатов одним методом

Я использую NUnit 2.5 и хочу протестировать метод, который получает несколько параметров.
Разумно ли попробовать протестировать их, используя один метод тестирования, с несколькими разными тестовыми наборами, или его будет сложнее поддерживать (или просто закрепите указать точный провал) в конечном итоге?

Моя общая идея - попробовать что-то вроде

[TestCase("valid", Result="validasd", 
  Description = "Valid string test, expecting valid answer")]
[TestCase(null, ExpectedException = typeof(ArgumentNullException),
  Description = "Calling with null, expecting exception")]
[TestCase("", ExpectedException = typeof(ArgumentException), 
  Description = "Calling with an empty string, expecting exception")]
public string TestSubString(string value)
{
  return MethodUnderTest(value);
}

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


person Noam Gal    schedule 26.05.2009    source источник


Ответы (5)


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

Этот подход не работает, когда для метода требуются сложные параметры объекта, поскольку атрибут TestCase может принимать только примитивные типы в качестве входных параметров для передачи методу.

Вы также можете подумать о написании более типичного модульного теста, если:

  1. другие разработчики в вашей команде не знакомы с этой техникой
  2. подпись метода, который может изменить или приспособить больше параметров
  3. если есть более чем несколько тестовых примеров или комбинаций значений для тестирования
  4. вам нужно, чтобы тесты выполнялись в определенном порядке
  5. вы хотите запускать свои тесты с помощью ReSharper для Visual Studio (в настоящее время он не распознает Атрибут TestCase)
person LBushkin    schedule 26.05.2009
comment
Я также написал несколько тестов с более сложными параметрами и возвращаемыми значениями, используя атрибут TestCaseSource, указывающий на поле IEnumerable ‹TestCaseData›. Я думаю, что это было чересчур - таким образом, данные и, что более важно, логика утверждений далеки от самого теста, что определенно затруднит их понимание. Спасибо за ваш вклад. (И всем остальным) - person Noam Gal; 26.05.2009

Я бы этого не сделал. Это было бы крайне сложно поддерживать. У меня был бы один метод тестирования для каждого случая.

person Daniel A. White    schedule 26.05.2009
comment
Как вы думаете, почему такой подход сложно поддерживать? Он выделяет вызовы метода и поясняет, что это вариации теста. Единственный недостаток, который я вижу (который поддерживает вашу точку зрения), заключается в том, что большинство инструментов рефакторинга не могут изменять такие атрибуты при изменении сигнатуры метода. - person LBushkin; 26.05.2009

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

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

person David Nelson    schedule 26.05.2009

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

  • исполнитель тестов сгруппирует эти методы в одном узле дерева
  • визуально очевидно, что эти 3 тестовых случая применяются к одному и тому же коду, изменяются только параметры метода, и если тест не проходит, сообщение об ошибке все равно сообщит вам, какие параметры привели к сбою
  • меньше кода!

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

person DonkeyMaster    schedule 26.05.2009

Не лучше ли написать что-нибудь подобное?

[Test]
public string TestSubString()
{
    Assert.AreEqual("validasd", MethodUnderTest("valid"));
    AssertEx.Throws<ArgumentNullException>(() => { MethodUnderTest(null); });
    AssertEx.Throws<ArgumentException>(() => { MethodUnderTest(""); });
}
person Alex Kofman    schedule 26.05.2009