Библиотеки F# модульного тестирования с помощью dotnet test и NUnit

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

Эта статья посвящена тестированию проекта .NET Core. Если вы тестируете проект ASP.NET Core, ознакомьтесь с тестами интеграции в ASP.NET Core.

Необходимые компоненты

  • Пакет SDK для .NET 8 или более поздние версии.
  • Текстовый редактор или редактор кода по вашему выбору.

Создание исходного проекта

Откройте окно оболочки. Создайте каталог с именем unit-testing-with-fsharp для хранения решения. В этом каталоге выполните следующую команду, чтобы создать файл решения для библиотеки классов и тестового проекта:

dotnet new sln

Затем создайте каталог MathService. Ниже приведена актуальная структура каталогов и файлов:

/unit-testing-with-fsharp
    unit-testing-with-fsharp.sln
    /MathService

Перейдите к каталогу MathService и выполните следующую команду, чтобы создать исходный проект:

dotnet new classlib -lang "F#"

Создайте реализацию службы вычислений со сбоем:

module MyMath =
    let squaresOfOdds xs = raise (System.NotImplementedException("You haven't written a test yet!"))

Вернитесь в каталог unit-testing-with-fsharp. Чтобы добавить проект библиотеки классов в решение, выполните следующую команду:

dotnet sln add .\MathService\MathService.fsproj

Создание тестового проекта

Затем создайте каталог MathService.Tests. Ниже представлена структура каталогов:

/unit-testing-with-fsharp
    unit-testing-with-fsharp.sln
    /MathService
        Source Files
        MathService.fsproj
    /MathService.Tests

Перейдите к каталогу MathService.Tests и создайте проект, выполнив следующую команду:

dotnet new nunit -lang "F#"

Эта команда создает тестовый проект, использующий NUnit в качестве платформы тестирования. Созданный шаблон настраивает средство выполнения тестов в файле MathServiceTests.fsproj:

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
  <PackageReference Include="NUnit" Version="4.1.0" />
  <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

Тестовый проект требует других пакетов для создания и выполнения модульных тестов. Команда dotnet new на предыдущем шаге добавляет NUnit и адаптер тестирования NUnit. Теперь добавьте в проект библиотеку классов MathService в качестве еще одной зависимости. Используйте команду dotnet add reference:

dotnet add reference ../MathService/MathService.fsproj

Все содержимое файла можно просмотреть в репозитории образцов на сайте GitHub.

Ниже показан окончательный макет решения:

/unit-testing-with-fsharp
    unit-testing-with-fsharp.sln
    /MathService
        Source Files
        MathService.fsproj
    /MathService.Tests
        Test Source Files
        MathService.Tests.fsproj

Выполните следующую команду в каталоге unit-testing-with-fsharp:

dotnet sln add .\MathService.Tests\MathService.Tests.fsproj

Создание первого теста

Напишите один тест сбоя теста, запустите его, а затем повторите этот процесс. Откройте файл UnitTest1.fs и добавьте следующий код:

namespace MathService.Tests

open System
open NUnit.Framework
open MathService

[<TestFixture>]
type TestClass () =

    [<Test>]
    member this.TestMethodPassing() =
        Assert.That(true, Is.True)

    [<Test>]
     member this.FailEveryTime() = Assert.That(false, Is.True)

Атрибут [<TestFixture>] обозначает класс, который содержит тесты. Атрибут [<Test>] обозначает метод теста, который выполняется с помощью средства выполнения тестов. В каталоге unit-testing-with-fsharp выполните команду dotnet test для создания тестов и библиотеки классов, а затем выполните тесты. Средство запуска тестов NUnit содержит точку входа в программу для выполнения тестов. dotnet test запускает средство выполнения тестов с помощью проекта модульного теста, который вы создали.

Эти два теста демонстрируют самые простые тесты, которые выполняются и завершаются сбоем. My test выполняется успешно, а Fail every time завершается сбоем. Сейчас создайте тест для метода squaresOfOdds. Метод squaresOfOdds возвращает последовательность квадратов всех нечетных целочисленных значений, которые являются частью входной последовательности. Вместо того чтобы писать все эти функции одновременно, можно постепенно создавать тесты, проверяющие функциональность. Чтобы сделать каждый тестовый проход означает, вы создадите необходимые функциональные возможности для метода.

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

[<Test>]
member this.TestEvenSequence() =
    let expected = Seq.empty<int>
    let actual = MyMath.squaresOfOdds [2; 4; 6; 8; 10]
    Assert.That(actual, Is.EqualTo(expected))

Обратите внимание, что expected последовательность преобразуется в список. Платформа NUnit зависит от многих стандартных типов .NET. Эта зависимость означает, что ваш открытый интерфейс и ожидаемые результаты поддерживают интерфейс ICollection, а не IEnumerable.

Когда вы запустите этот тест, он завершится сбоем. Это связано с тем, что вы еще не создали реализацию. Чтобы этот тест прошел успешно, напишите простейший код в классе Library.fs в своем рабочем проекте MathService:

let squaresOfOdds xs =
    Seq.empty<int>

В каталоге unit-testing-with-fsharp снова выполните команду dotnet test. Команда dotnet test запускает сборку для проекта MathService и затем для проекта MathService.Tests. После сборки обоих проектов команда позволяет запустить ваши тесты. Теперь два теста пройдены.

Выполнение требований

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

[<Test>]
member public this.TestOnesAndEvens() =
    let expected = [1; 1; 1; 1]
    let actual = MyMath.squaresOfOdds [2; 1; 4; 1; 6; 1; 8; 1; 10]
    Assert.That(actual, Is.EqualTo(expected))

Когда вы запустите dotnet test, новый тест завершится ошибкой. Необходимо обновить метод squaresOfOdds, чтобы обработать этот новый тест. Чтобы тест выполнялся успешно, необходимо отфильтровать все четные числа из последовательности. Чтобы сделать это, напишите небольшую функцию фильтра и используйте Seq.filter:

let private isOdd x = x % 2 <> 0

let squaresOfOdds xs =
    xs
    |> Seq.filter isOdd

Нужно выполнить еще один шаг: определить квадрат каждого из нечетных чисел. Начнем с написания нового теста:

[<Test>]
member public this.TestSquaresOfOdds() =
    let expected = [1; 9; 25; 49; 81]
    let actual = MyMath.squaresOfOdds [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
    Assert.That(actual, Is.EqualTo(expected))

Чтобы вычислить квадрат каждого нечетного числа, можно исправить тест, пропустив фильтрованную последовательность через операцию сопоставления:

let private square x = x * x
let private isOdd x = x % 2 <> 0

let squaresOfOdds xs =
    xs
    |> Seq.filter isOdd
    |> Seq.map square

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

См. также