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


Модульное тестирование C# в .NET с помощью dotnet test и xUnit

В этом руководстве показано, как создать решение, содержащее проект модульного теста и проект исходного кода. Чтобы выполнить руководство с помощью предварительно созданного решения, просмотрите или скачайте пример кода. Инструкции по скачиванию смотрите в разделах Образцы и руководства.

Создание решения

В этом разделе создается решение, содержащее исходные и тестовые проекты. Готовое решение имеет следующую структуру каталогов:

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService
        PrimeService.cs
        PrimeService.csproj
    /PrimeService.Tests
        PrimeService_IsPrimeShould.cs
        PrimeServiceTests.csproj

Ниже приведены инструкции по созданию тестового решения. Команды для создания тестового решения, чтобы сделать это в один шаг.

  • Откройте окно терминала.

  • Выполните следующую команду:

    dotnet new sln -o unit-testing-using-dotnet-test
    

    Команда dotnet new sln создает новое решение в каталоге unit-testing-using-dotnet-test .

  • Измените каталог на папку unit-testing-using-dotnet-test .

  • Выполните следующую команду:

    dotnet new classlib -o PrimeService
    

    Команда dotnet new classlib создает проект библиотеки классов в папке PrimeService . Новая библиотека классов будет содержать тестируемый код.

  • Переименуйте Class1.cs в PrimeService.cs.

  • Замените код в PrimeService.cs следующим кодом:

    using System;
    
    namespace Prime.Services
    {
        public class PrimeService
        {
            public bool IsPrime(int candidate)
            {
                throw new NotImplementedException("Not implemented.");
            }
        }
    }
    

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

  • В каталоге unit-testing-using-dotnet-test выполните следующую команду, чтобы добавить проект библиотеки классов в решение:

    dotnet sln add ./PrimeService/PrimeService.csproj
    
  • Создайте проект PrimeService.Tests , выполнив следующую команду:

    dotnet new xunit -o PrimeService.Tests
    

    Предыдущая команда создает проект PrimeService.Tests в каталоге PrimeService.Tests. Тестовый проект использует xUnit в качестве тестовой библиотеки. Команда также настраивает средство выполнения теста, добавив следующие <PackageReference />элементы в файл проекта:

    • Microsoft.NET.Test.Sdk
    • xunit
    • xunit.runner.visualstudio
    • coverlet.collector
  • Добавьте тестовый проект в файл решения, выполнив следующую команду:

    dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
    
  • Добавьте библиотеку PrimeService классов в качестве зависимости в проект PrimeService.Tests :

    dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj
    

Команды для создания решения

В этом разделе перечислены все команды, описанные в предыдущем разделе. Пропустите этот раздел, если вы выполнили действия, описанные в предыдущем разделе.

Следующие команды создают тестовое решение на компьютере Windows. Для macOS и Unix обновите ren команду до версии ren ОС, чтобы переименовать файл:

dotnet new sln -o unit-testing-using-dotnet-test
cd unit-testing-using-dotnet-test
dotnet new classlib -o PrimeService
ren .\PrimeService\Class1.cs PrimeService.cs
dotnet sln add ./PrimeService/PrimeService.csproj
dotnet new xunit -o PrimeService.Tests
dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj
dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj

Выполните инструкции по замене кода в PrimeService.cs следующим кодом в предыдущем разделе.

Создайте тест

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

Обновите проект PrimeService.Tests :

  • Удаление PrimeService.Tests/UnitTest1.cs.

  • Создайте файл PrimeService.Tests/PrimeService_IsPrimeShould.cs .

  • Замените код в PrimeService_IsPrimeShould.cs следующим кодом:

    using Xunit;
    using Prime.Services;
    
    namespace Prime.UnitTests.Services
    {
        public class PrimeService_IsPrimeShould
        {
            [Fact]
            public void IsPrime_InputIs1_ReturnFalse()
            {
                var primeService = new PrimeService();
                bool result = primeService.IsPrime(1);
    
                Assert.False(result, "1 should not be prime");
            }
        }
    }
    

Атрибут [Fact] объявляет метод тестирования, который выполняется тестовым раннером. В папке PrimeService.Tests выполните команду dotnet test. Команда dotnet test создает оба проекта и запускает тесты. Запускатель тестов xUnit содержит точку входа программы для выполнения тестов. dotnet test запускает средство выполнения теста с помощью проекта модульного теста.

Тест завершается ошибкой, так как IsPrime не был реализован. Используя подход TDD, напишите только достаточно кода, чтобы этот тест прошел. Обновите IsPrime, включив в него следующий код.

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

Выполните dotnet test. Тест пройден.

Добавьте дополнительные тесты.

Добавьте тесты праймерных чисел для 0 и -1. Можно скопировать тест, созданный на предыдущем шаге, и создать копии следующего кода, чтобы проверить 0 и -1. Но не делай это, так как есть лучший способ.

var primeService = new PrimeService();
bool result = primeService.IsPrime(1);

Assert.False(result, "1 should not be prime");

Копирование тестового кода, когда изменяется только один параметр, приводит к дублированию кода и разрастанию тестов. Следующие атрибуты xUnit позволяют создавать набор аналогичных тестов:

  • [Theory] представляет набор тестов, которые выполняют один и тот же код, но имеют разные входные аргументы.
  • [InlineData] атрибут задает значения для этих входных данных.

Вместо создания новых тестов примените предыдущие атрибуты xUnit для создания одной теории. Замените следующий код...

[Fact]
public void IsPrime_InputIs1_ReturnFalse()
{
    var primeService = new PrimeService();
    bool result = primeService.IsPrime(1);

    Assert.False(result, "1 should not be prime");
}

... с помощью следующего кода:

[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
    var result = _primeService.IsPrime(value);

    Assert.False(result, $"{value} should not be prime");
}

В приведенном выше коде [Theory] и [InlineData] включите тестирование нескольких значений менее двух. Два — наименьшее первое число.

Добавьте следующий код после объявления класса и перед атрибутом [Theory] :

private readonly PrimeService _primeService;

public PrimeService_IsPrimeShould()
{
    _primeService = new PrimeService();
}

Выполните dotnet test, и два теста не проходят. Чтобы выполнить все тесты, обновите IsPrime метод следующим кодом:

public bool IsPrime(int candidate)
{
    if (candidate < 2)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

После подхода TDD добавьте более неудачные тесты, а затем обновите целевой код. Ознакомьтесь с готовой версией тестов и полной реализацией библиотеки.

Завершенный IsPrime метод не является эффективным алгоритмом для тестирования примиальности.

Дополнительные ресурсы