15. Тестовые случаи
Цели обучения
В этом разделе будут рассмотрены атрибуты [TestCase] и аналогичные атрибуты NUnit и как работать с ними в UnityTests.
Введение и мотивация
NUnit имеет несколько инструментов для параметризированных тестов, которые могут быть использованы для определения тестовых случаев с различными параметрами. Это может резко сократить количество повторяющегося кода и сделать тест чистым для использования.
Пример параметризированного теста с использованием атрибута [TestCase]:
[Test]
[TestCase(49, "a string", true)]
[TestCase(9, "something", false)]
public void MyTest(int firstValue, string secondValue, bool expectedOutcome)
{
...
}
Это создаст два теста, каждый с разным входом в тело метода.
В дополнение к атрибуту [TestCase], NUnit также имеет атрибут [Values], который указывает набор значений на каждом отдельном входе. Примером такого атрибута является:
[Test]
public void MyTest([Values(49, 9)]int firstValue, [Values("a string", "something")]string secondValue)
{
...
}
При указании нескольких входных параметров, они рассматриваются как комбинационные. Это означает, что каждая комбинация из них будет проверена. Для вышеприведенного примера это приведет к 4 случаям:
MyTest(49, "a string")
MyTest(49, "something")
MyTest(9, "a string")
MyTest(9, "something")
Это может легко взорваться во многих комбинациях. Комбинации могут быть не все ценные и просто тратить время, так что используйте это с осторожностью.
Для обоих [TestCase] и [Values] атрибутов, существует более динамичная версия, называемая [TestCaseSource] и [ValueSource] соответственно. Каждый из них принимает статический метод или массив и возвращает коллекцию объектов.
Из этих 4 методов, [ValueSource] атрибут в настоящее время является единственным, поддерживаемым [UnityTest]Поскольку это приведет к комбинированным тестам, если несколько аргументов с [ValueSource] заданы, то для теста с несколькими аргументами рекомендуется создать структуру тестового случая. Выглядеть это может так:
[UnityTest]
public IEnumerator AddAsyncCalculatesCorrectValue([ValueSource(nameof(TestCases))] TestCase testCase)
{
...
}
private static IEnumerable TestCases()
{
yield return new TestCase {value1 = 4, value2 = "a string"};
yield return new TestCase {value1 = 8, value2 = "another string"};
}
public struct TestCase
{
public int value1;
public string value2;
public override string ToString()
{
return $"{value1}, {value2}";
}
}
2010 год
В течение образец 15_TestCases класс с несложной математикой. У него два метода:
Add, которая берёт два целых числа и суммирует их.AddAsyncтакже суммирует два целых числа, но делает это асинхронно, возвращаяIEnumerator
Задача состоит в том, чтобы добавить тесты для двух методов. Метод AddAsync сначала возвращает результат после нескольких кадров, так что это будет лучше всего подходить для [UnityTest]. Обратите внимание, что не достаточно возвращать IEnumerator, так как тестовый фреймворк сейчас не поддерживает вложенные возвраты. Вместо этого создайте цикл и продвигайтесь по каждому элементу, пока работа не будет завершена. На каждой итерации цикла while тест должен возвращать null.
Подсказки
-
ToString()реализация в структуре нужна, чтобы в дереве тестового раннера отображались понятные сведения. Без неё для каждого случая в качестве аргумента теста показывалось бы просто имя структуры.
Решение
Решение для этого упражнения можно найти в примере 15_TestCases_Solution. Тесты для обоих методов могут быть реализованы следующим образом:
[Test]
[TestCase(24, 80, 104)]
[TestCase(10, -15, -5)]
[TestCase(int.MaxValue, 10, int.MinValue + 9)]
public void AddCalculatesCorrectValue(int valueA, int valueB, int expectedResult)
{
var myClass = new MyClass();
var result = myClass.Add(valueA, valueB);
Assert.That(result, Is.EqualTo(expectedResult));
}
[UnityTest]
public IEnumerator AddAsyncCalculatesCorrectValue([ValueSource(nameof(AdditionCases))] AddCase addCase)
{
var myClass = new MyClass();
var enumerator = myClass.AddAsync(addCase.valueA, addCase.valueB);
while (enumerator.MoveNext())
{
yield return null;
}
var result = enumerator.Current;
Assert.That(result, Is.EqualTo(addCase.expectedResult));
}
private static IEnumerable AdditionCases()
{
yield return new AddCase {valueA = 24, valueB = 80, expectedResult = 104};
yield return new AddCase {valueA = 10, valueB = -15, expectedResult = -5};
yield return new AddCase {valueA = int.MaxValue, valueB = 10, expectedResult = int.MinValue + 9};
}
public struct AddCase
{
public int valueA;
public int valueB;
public int expectedResult;
public override string ToString()
{
return $"{valueA} + {valueB} = {expectedResult}";
}
}