.NET CLI を使用したプロジェクトの整理とテスト

このチュートリアルでは、「チュートリアル: Visual Studio Code を使用して .NET コンソール アプリケーションを作成する」に従って、簡単なコンソール アプリの作成より先に進んで、高度でよく構成されたアプリケーションの開発を行います。 フォルダーを使用してコードを整理する方法に続き、このチュートリアルでは xUnit テスト フレームワークでコンソール アプリケーションを拡張する方法を示します。

Note

このチュートリアルでは、アプリケーション プロジェクトとテスト プロジェクトを別々のフォルダーに配置することをお勧めします。 一部の開発者は、これらのプロジェクトを同じフォルダーに保持することを好みます。 詳細については、GitHub イシュー dotnet/docs #26395 を参照してください。

フォルダーを使用してコードを整理する

コンソール アプリに新しいタイプを導入する場合、そのタイプを含むファイルをアプリに追加することができます。 たとえば、AccountInformation および MonthlyReportRecords タイプを含むファイルをプロジェクトに追加した場合、以下のように、プロジェクト ファイルの構造はフラットで移動しやすいものになります。

/MyProject
|__AccountInformation.cs
|__MonthlyReportRecords.cs
|__MyProject.csproj
|__Program.cs

ただし、このフラット構造はプロジェクトのサイズが比較的小さい場合にのみ適しています。 プロジェクトに 20 個のタイプを追加した場合はどうなるかというと、 プロジェクトのルート ディレクトリが乱雑になり、その多くのファイルの移動や維持が容易でなくなることは明らかです。

このようなプロジェクトを整理するには、新しいフォルダーを作成し、Models という名前を付けてタイプ ファイルを保持します。 以下のように、Models フォルダーにタイプ ファイルを配置します。

/MyProject
|__/Models
   |__AccountInformation.cs
   |__MonthlyReportRecords.cs
|__MyProject.csproj
|__Program.cs

論理的にファイルをフォルダーにグループ化するプロジェクトでは、移動や維持が容易になります。 次のセクションでは、フォルダーと単体テストを使用してさらに複雑なサンプルを作成します。

NewTypes ペット サンプルを使用した整理とテスト

前提条件

サンプルのビルド

以下の手順では、NewTypes ペット サンプルに従うことも、独自のファイルとフォルダーを作成することもできます。 タイプは、後でタイプをさらに追加することができるフォルダー構造に論理的に整理されます。また、テストも、後でテストをさらに追加できるフォルダーに論理的に配置されます。

サンプルには DogCat という 2 つのタイプが含まれています。このサンプルでは IPet という共通インターフェイスを実装します。 NewTypes プロジェクトの目標は、Pets フォルダーにペット関連のタイプを整理することです。 別のタイプ セット (WildAnimals など) が後で追加された場合、それらは Pets フォルダーと同じ場所にある NewTypes フォルダーに配置されます。 WildAnimals フォルダーには、SquirrelRabbit タイプなど、ペットではない動物のタイプを含めることができます。 このようにタイプを追加すれば、プロジェクトはよく構成された状態に保たれます。

以下のようなファイル コンテンツのフォルダー構造を作成します。

/NewTypes
|__/src
   |__/NewTypes
      |__/Pets
         |__Dog.cs
         |__Cat.cs
         |__IPet.cs
      |__Program.cs
      |__NewTypes.csproj

IPet.cs:

using System;

namespace Pets
{
    public interface IPet
    {
        string TalkToOwner();
    }
}

Dog.cs:

using System;

namespace Pets
{
    public class Dog : IPet
    {
        public string TalkToOwner() => "Woof!";
    }
}

Cat.cs:

using System;

namespace Pets
{
    public class Cat : IPet
    {
        public string TalkToOwner() => "Meow!";
    }
}

Program.cs:

using System;
using Pets;
using System.Collections.Generic;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            List<IPet> pets = new List<IPet>
            {
                new Dog(),
                new Cat()
            };

            foreach (var pet in pets)
            {
                Console.WriteLine(pet.TalkToOwner());
            }
        }
    }
}

NewTypes.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

次のコマンドを実行します。

dotnet run

次の出力を取得します。

Woof!
Meow!

省略可能な演習:このプロジェクトを拡張し、Bird などの新しいペット タイプを追加することができます。 その場合、鳥の TalkToOwner メソッドで所有者に Tweet! を与えるようにします。 再度アプリを実行します。 出力には Tweet! が含まれます。

サンプルのテスト

これで、NewTypes プロジェクトの準備ができました。フォルダーにはペット関連のタイプが保持されており、プロジェクトは整理された状態です。 次は、テスト プロジェクトを作成し、xUnit テスト フレームワークを使用してテストの作成を開始します。 単体テストでは、ペット タイプの動作を自動的にチェックして、正しく動作していることを確認することができます。

src フォルダーに移動して、test フォルダーを作成します。この中に NewTypesTests フォルダーが含まれます。 NewTypesTests フォルダーのコマンド プロンプトから、dotnet new xunit を実行します。 このコマンドにより、NewTypesTests.csprojUnitTest1.cs という 2 つのファイルが生成されます。

現在、テスト プロジェクトでは NewTypes のタイプをテストすることはできません。NewTypes プロジェクトへのプロジェクト参照が必要になります。 プロジェクト参照を追加するには、以下の dotnet add reference コマンドを使用します。

dotnet add reference ../../src/NewTypes/NewTypes.csproj

または、次のように、NewTypesTests.csproj ファイルに <ItemGroup> ノードを追加して、プロジェクト参照を手動で追加することもできます。

<ItemGroup>
  <ProjectReference Include="../../src/NewTypes/NewTypes.csproj" />
</ItemGroup>

NewTypesTests.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
    <PackageReference Include="xunit" Version="2.8.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="../../src/NewTypes/NewTypes.csproj"/>
  </ItemGroup>

</Project>

NewTypesTests.csproj ファイルには次のパッケージ参照が含まれます。

  • Microsoft.NET.Test.Sdk (.NET テスト インフラストラクチャ)
  • xunit (xUnit テスト フレームワーク)
  • xunit.runner.visualstudio (テスト ランナー)
  • NewTypes (テストするコード)

UnitTest1.cs の名前を PetTests.cs に変更し、ファイル内のコードを次のコードで置き換えます。

using System;
using Xunit;
using Pets;

public class PetTests
{
    [Fact]
    public void DogTalkToOwnerReturnsWoof()
    {
        string expected = "Woof!";
        string actual = new Dog().TalkToOwner();

        Assert.NotEqual(expected, actual);
    }

    [Fact]
    public void CatTalkToOwnerReturnsMeow()
    {
        string expected = "Meow!";
        string actual = new Cat().TalkToOwner();

        Assert.NotEqual(expected, actual);
    }
}

省略可能な演習:所有者に Tweet! を与える前述の Bird タイプを追加した場合は、テスト メソッドを PetTests.cs ファイル BirdTalkToOwnerReturnsTweet に追加し、Bird タイプに対して TalkToOwner メソッドが正しく動作することを確認します。

Note

expectedactual の値は等しくなることが予想されますが、Assert.NotEqual チェックに対する初期アサーションでは、これらの値が等しくないと指定されています。 通常、テストのロジックを確認するために、最初は一度失敗するテストを作成します。 テストが失敗したことを確認したら、テストに合格できるようにするアサーションを調整します。

完全なプロジェクト構造を次に示します。

/NewTypes
|__/src
   |__/NewTypes
      |__/Pets
         |__Dog.cs
         |__Cat.cs
         |__IPet.cs
      |__Program.cs
      |__NewTypes.csproj
|__/test
   |__NewTypesTests
      |__PetTests.cs
      |__NewTypesTests.csproj

test/NewTypesTests ディレクトリから開始します。 dotnet test コマンドを使用して、テストを実行します。 このコマンドは、プロジェクト ファイルで指定されたテスト ランナーを開始します。

予想どおり、テストは失敗し、コンソールには次の出力が表示されます。

Test run for C:\Source\dotnet\docs\samples\snippets\core\tutorials\testing-with-cli\csharp\test\NewTypesTests\bin\Debug\net5.0\NewTypesTests.dll (.NETCoreApp,Version=v5.0)
Microsoft (R) Test Execution Command Line Tool Version 16.8.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.50]     PetTests.DogTalkToOwnerReturnsWoof [FAIL]
  Failed PetTests.DogTalkToOwnerReturnsWoof [6 ms]
  Error Message:
   Assert.NotEqual() Failure
Expected: Not "Woof!"
Actual:   "Woof!"
  Stack Trace:
     at PetTests.DogTalkToOwnerReturnsWoof() in C:\Source\dotnet\docs\samples\snippets\core\tutorials\testing-with-cli\csharp\test\NewTypesTests\PetTests.cs:line 13

Failed!  - Failed:     1, Passed:     1, Skipped:     0, Total:     2, Duration: 8 ms - NewTypesTests.dll (net5.0)

テストのアサーションを Assert.NotEqual から Assert.Equal に変更します。

using System;
using Xunit;
using Pets;

public class PetTests
{
    [Fact]
    public void DogTalkToOwnerReturnsWoof()
    {
        string expected = "Woof!";
        string actual = new Dog().TalkToOwner();

        Assert.Equal(expected, actual);
    }

    [Fact]
    public void CatTalkToOwnerReturnsMeow()
    {
        string expected = "Meow!";
        string actual = new Cat().TalkToOwner();

        Assert.Equal(expected, actual);
    }
}

dotnet test を使用してテストを再実行し、次の出力を取得します。

Test run for C:\Source\dotnet\docs\samples\snippets\core\tutorials\testing-with-cli\csharp\test\NewTypesTests\bin\Debug\net5.0\NewTypesTests.dll (.NETCoreApp,Version=v5.0)
Microsoft (R) Test Execution Command Line Tool Version 16.8.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 2 ms - NewTypesTests.dll (net5.0)

テストに成功します。 ペット タイプのメソッドは、所有者との対話中に正しい値を返します。

これで、xUnit を使用してプロジェクトを整理し、テストする方法を習得できました。 次は、これらの方法を独自のプロジェクトに適用します。 コーディングを楽しんでください。