создание модульного теста, управляемого данными

Платформу модульных тестов Майкрософт (MSTest) можно использовать для управляемого кода, чтобы настроить метод модульного теста для получения значений из источника данных. Этот метод последовательно выполняется для каждой строки в источнике данных, что облегчает тестирование разнообразных входных данных с помощью одного метода.

Модульный тест на основе данных может использовать любой из следующих типов:

  • встроенные данные с помощью атрибута DataRow
  • данные-члены с помощью атрибута DynamicData
  • из некоторых известных поставщиков источников с помощью атрибута DataSource

Тестируемый метод

Например, допустим, что у вас есть:

  1. решение с именем MyBank, которое принимает и обрабатывает транзакции для различных типов счетов;

  2. проект в MyBank с именем BankDb, который управляет транзакциями для счетов;

  3. класс с именем Maths в проекте BankDb, выполняющий математические функции, позволяющие убедиться в том, что любая транзакция выгодна банку;

  4. проект модульного теста с именем BankDbTests для тестирования функциональности компонента BankDb;

  5. класс модульного теста с именем MathsTests для проверки функциональности класса Maths.

Мы проверяем метод в Maths, который складывает два целых числа с помощью цикла:

public int AddIntegers(int first, int second)
{
    int sum = first;
    for (int i = 0; i < second; i++)
    {
        sum += 1;
    }

    return sum;
}

Тестовый метод теста

Встроенный тест на основе данных

Для встроенных тестов MSTest используется DataRow для указания значений, используемых тестом на основе данных. Тест в этом примере выполняется последовательно для каждой строки данных.

[TestMethod]
[DataRow(1, 1, 2)]
[DataRow(2, 2, 4)]
[DataRow(3, 3, 6)]
[DataRow(0, 0, 1)] // The test run with this row fails
public void AddIntegers_FromDataRowTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Тест на основе данных участника

MSTest использует DynamicData атрибут для указания имени, типа (свойства, по умолчанию или метода) и определения типа (по умолчанию используется текущий тип) элемента, который предоставит данные, используемые тестом на основе данных.

public static IEnumerable<object[]> AdditionData
{
    get
    {
        return new[]
        { 
            new object[] { 1, 1, 2 },
            new object[] { 2, 2, 4 },
            new object[] { 3, 3, 6 },
            new object[] { 0, 0, 1 }, // The test run with this row fails
        };
    }
}

[TestMethod]
[DynamicData(nameof(AdditionData))]
public void AddIntegers_FromDynamicDataTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Также можно переопределить созданное по умолчанию отображаемое имя, используя DynamicDataDisplayName свойство атрибута DynamicData . Сигнатура метода отображаемого имени должна быть и public static string принимать два параметра: первый тип MethodInfo и второй тип object[].

public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data)
{
    return string.Format("DynamicDataTestMethod {0} with {1} parameters", methodInfo.Name, data.Length);
}

[DynamicData(nameof(AdditionData), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))]

Тест на основе данных поставщика источника

Создание модульного теста на основе источника данных включает в себя следующие действия.

  1. Создайте источник данных, в котором содержатся значения, используемые в методе теста. Источник данных может быть любого типа, который зарегистрирован на компьютере, где выполняется тест.

  2. Добавьте общедоступное TestContext свойство типа TestContext в тестовый класс.

  3. Создание метода модульного теста

  4. DataSourceAttribute Добавьте в него атрибут.

  5. Используйте свойство индексатора DataRow для получения значений, используемых в тесте.

Создание источника данных

Чтобы проверить метод AddIntegers, создайте источник данных, определяющий диапазон значений для параметров и ожидаемую возвращаемую сумму. В этом примере мы создадим базу данных SQL Compact с именем MathsData и таблицу с именем AddIntegersData, которая содержит следующие имена столбцов и значения:

FirstNumber SecondNumber Sum
0 1 1
1 1 2
2 -3 -1

Добавление TestContext в тестовый класс

Платформа модульного тестирования создает объект TestContext для хранения информации из источника данных для теста, управляемого данными. Затем платформа присваивает этому объекту значение создаваемого свойства TestContext.

public TestContext TestContext { get; set; }

В методе теста получить доступ к данным можно через свойство индексатора DataRowTestContext.

Примечание.

.NET Core не поддерживает атрибут DataSource. Если вы пытаетесь получить доступ к тестируемым данным таким образом в проекте модульного теста .NET Core, UWP или WinUI, появится ошибка, аналогичная "TestContext" не содержит определения для DataRow и метода расширения DataRow, принимающий первый аргумент типа TestContext (отсутствует директива using или ссылка на сборку?)".

Написание метода теста

Метод теста для AddIntegers достаточно прост. Для каждой строки в источнике данных вызовите AddIntegers со значениями столбцов FirstNumber и SecondNumber в качестве параметров и сверьте возвращаемое значение со значением столбца Sum:

[TestMethod]
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")]
public void AddIntegers_FromDataSourceTest()
{
    var target = new Maths();

    // Access the data
    int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
    int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
    int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Определение DataSourceAttribute

Атрибут DataSource задает строку подключения для источника данных и имя таблицы, которая используется в методе теста. Точные сведения в строке подключения отличаются в зависимости от типа используемого источника данных. В этом примере используется база данных SqlServerCe.

[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0;Data Source=C:\Data\MathsData.sdf", "AddIntegersData")]

Атрибут DataSource имеет три конструктора.

[DataSource(dataSourceSettingName)]

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

Использование файла app.config позволяет менять расположение источника данных и при этом не вносить изменения в сам модульный тест. Дополнительные сведения о создании и использовании файла app.config см. в статье Пошаговое руководство. Использование файла конфигурации для определения источника данных.

[DataSource(connectionString, tableName)]

Конструктор DataSource с двумя параметрами задает строку подключения к источнику данных и имя таблицы, содержащей данные для метода теста.

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

[DataSource(
    dataProvider,
    connectionString,
    tableName,
    dataAccessMethod
    )]

Использование TestContext.DataRow для доступа к данным

Для доступа к данным в таблице AddIntegersData используйте индексатор TestContext.DataRow. DataRow является объектом DataRow, поэтому значения столбца извлекаются по индексу или имени столбца. Так как значения возвращаются в виде объектов, преобразуйте их в соответствующий тип.

int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);

Запуск теста и просмотр результатов

Завершив написание метода теста, выполните сборку тестового проекта. Метод теста приводится в обозревателе тестов в группе Не запускавшиеся тесты. При запуске, написании и повторном запуске тестов в обозревателе тестов результаты отображаются в группах Неудачные тесты, Пройденные тесты и Незапускавшиеся тесты. Можно выбрать Запустить все , чтобы запустить все тесты, или выбрать Запустить , чтобы выбрать подмножество тестов для запуска.

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

Примечание.

Показан результат для каждой строки данных, а также один сводный результат. Если тест пройден для каждой строки данных, в сводке отображается Пройдено. Если тест не пройден для какой-то строки данных, в сводке отображается Не пройдено.

Если вы выполнили любой из AddIntegers_FromDynamicDataTestAddIntegers_FromDataRowTestметодов или AddIntegers_FromDataSourceTest метода в нашем примере, панель результатов становится красной, а метод теста перемещается в неудачные тесты. Тест, управляемый данными, завершается с ошибкой, если какая-либо итерация из источника данных завершается неудачно. При выборе неудачных тестов, управляемых данными, в окне обозревателя тестов на панели сведений выводятся результаты каждой итерации в соответствии с индексом строки данных. В этом примере оказывается, что алгоритм AddIntegers неправильно обрабатывает отрицательные значения.

После исправления тестируемого метода и повторного выполнения теста панель результатов станет зеленой и метод теста переместится в группу Пройденные тесты.