Поделиться через


Создание модульного теста на основе данных

Платформу модульных тестов Майкрософт (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 для указания имени, типа и определения типа (по умолчанию используется текущий тип) элемента, который предоставит данные, используемые тестом на основе данных.

Заметка

До MSTest 3.8 перечисление DynamicDataSourceType имело два члена, Property и Method. Значение по умолчанию было Property. Начиная с MSTest 3.8 новый элемент AutoDetect добавляется в перечисление и по умолчанию. Поэтому вам больше не нужно указывать DynamicDataSourceType.

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 Второй номер Сумма
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 для решения. dataSourceSettingsName — это имя элемента Xml в файле конфигурации, указывающего сведения о подключении.

Использование файла 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_FromDataRowTest, AddIntegers_FromDynamicDataTest или AddIntegers_FromDataSourceTest в нашем примере, панель результатов становится красной, а метод теста перемещается в неудачных тестов. Тест на основе данных терпит неудачу, если любой из итерированных методов из источника данных не выполняется. При выборе теста на основе данных, который завершился неудачей, в окне обозревателя тестов на панели сведений отображаются результаты каждой итерации, идентифицированной по индексу строки данных. В нашем примере показано, что алгоритм AddIntegers неправильно обрабатывает отрицательные значения.

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