День 1510. #ЗаметкиНаПолях
Создание Параметризованных Тестов в xUnit
xUnit использует атрибуты для определения методов тестирования. Атрибут Fact определяет простой тест, а атрибут Theory определяет параметризованный тест. Допустим, у нас есть следующий тест, проверяющий, что парсер email корректно извлекает домен:
public void Should_Return_Domain(
string email, string expectedDomain)
{
var parser = new EmailParser();
var domain = parser.GetDomain(email);
Assert.Equal(domain, expectedDomain);
}
Рассмотрим 4 способа написания параметризованных тестов.
1. InlineData
Простейший способ - вы предоставляете тестовые данные, передавая значения конструктору:
[Theory]
[InlineData("[email protected]", "test.com")]
[InlineData("[email protected]", "github.io")]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }
Мы предоставляем два строковых значения для атрибута InlineData: аргумент тестируемого метода и ожидаемый результат. Можно указать атрибут InlineData столько раз, сколько будет тестовых случаев.
Недостаток в том, что код становится очень многословным, когда у нас много тестовых случаев. И мы ограничены использованием только константных данных для параметров.
2. MemberData
С помощью MemberData мы можем программно предоставить тестовые данные из статического свойства или члена типа:
[Theory]
[MemberData(nameof(EmailTestData))]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }
public static IEnumerable<object[]>
EmailTestData => new List<object>
{
new object[] { "[email protected]", "test.com" },
new object[] { "[email protected]", "github.io" }
};
Вы указываете имя члена в атрибуте MemberData. Одно ограничение - свойство (или метод) должно возвращать IEnumerable<object[]>, поэтому строгой типизации не существует.
3. ClassData
Позволяет извлечь тестовые данные в отдельный класс. Это полезно для организации тестовых данных отдельно от тестов и упрощает повторное использование. Вы загружаете тестовые данные из класса, который наследуется от IEnumerable<object[]> и реализует метод GetEnumerator:
[Theory]
[ClassData(typeof(EmailTestData))]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }
public class EmailTestData : IEnumerable<object[]>
{
public IEnumerable<object[]> GetEnumerator()
{
yield return new object[] {
"[email protected]", "test.com" };
yield return new object[] {
"[email protected]", "github.io" };
}
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
};
Этот подход сложен, потому что нужно реализовать интерфейс IEnumerable. И мы всё ещё страдаем от отсутствия безопасности типов.
4. TheoryData
Позволяет реализовать класс для тестовых данных, сохраняя при этом безопасность типов. Вот пример в сочетании с атрибутом ClassData, но его также можно использовать и с MemberData, возвращая TheoryData из свойства или метода:
[Theory]
[ClassData(typeof(EmailTestData))]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }
public class EmailTestData :
TheoryData<string, string>
{
public EmailTestData()
{
Add("[email protected]", "test.com");
Add("[email protected]", "github.io");
}
};
TheoryData – обобщённый класс, который позволяет указывать типы для параметризованного теста. Вы просто вызываете метод Add в конструкторе, чтобы предоставить тестовые данные для одного тестового примера. А добавление дополнительных тестов сводится к многократному вызову метода Add.
Источник: https://www.milanjovanovic.tech/blog/creating-data-driven-tests-with-xunit