次の方法で共有


dotnet テストと NUnit を使用した F# ライブラリの単体テスト

このチュートリアルでは、単体テストの概念を学習するためのサンプル ソリューションを段階的に構築する対話型エクスペリエンスについて説明します。 事前構築済みのソリューションを使用してチュートリアルに従う場合は、開始 する前にサンプル コードを表示またはダウンロード してください。 ダウンロード手順については、 サンプルとチュートリアルを参照してください。

この記事では、.NET Core プロジェクトのテストについて説明します。 もし ASP.NET Core プロジェクトをテストする場合は、ASP.NET Core の統合テストを参照してください。

[前提条件]

  • .NET 8 SDK 以降のバージョン。
  • 任意のテキスト エディターまたはコード エディター。

ソース プロジェクトを作成する

シェル ウィンドウを開きます。 ソリューションを保持するために 、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 reference add コマンドを使用します。

dotnet reference add ../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

最初のテストを作成する

失敗したテストを 1 つ記述し、合格させ、プロセスを繰り返します。 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 を実行すると、作成した単体テスト プロジェクトを使用してテスト ランナーが開始されます。

これら 2 つのテストは、最も基本的な合格テストと失敗したテストを示しています。 My test が渡され、 Fail every time が失敗します。 次に、 squaresOfOdds メソッドのテストを作成します。 squaresOfOdds メソッドは、入力シーケンスの一部であるすべての奇数整数値の 2 乗のシーケンスを返します。 これらの関数をすべて一度に記述するのではなく、機能を検証するテストを繰り返し作成できます。 各テスト パスの手段を作成するには、メソッドに必要な機能を作成します。

記述できる最も簡単なテストは、すべての偶数で 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をサポートすることを意味します。

テストを実行すると、テストが失敗することがわかります。 これは、実装をまだ作成していないためです。 動作する MathService プロジェクトの Library.fs クラスに最も単純なコードを記述して、このテストを成功させる。

let squaresOfOdds xs =
    Seq.empty<int>

unit-testing-with-fsharp ディレクトリでdotnet testをもう一度実行します。 dotnet test コマンドは、MathService プロジェクトのビルドを実行してから、MathService.Tests プロジェクトに対してビルドを実行します。 両方のプロジェクトをビルドすると、テストが実行されます。 2 つのテストが合格しました。

要件を満たす

テストが1つ成功したので、さらに多くのテストケースを書きましょう。 次の単純なケースは、唯一の奇数が1であるシーケンスで動作します。 1 の 2 乗は 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

残りのステップは、すべての奇数をそれぞれ2乗することです。 まず、新しいテストを記述します。

[<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))

テストを修正するには、フィルター処理されたシーケンスをマップ操作でパイプ処理し、各奇数の 2 乗を計算します。

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

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

あなたは、小さなライブラリとそのライブラリの単体テストのセットを構築しました。 新しいパッケージとテストの追加が通常のワークフローの一部になるように、ソリューションを構造化しました。 アプリケーションの目標を解決するために、ほとんどの時間と労力を集中しました。

こちらも参照ください