【C#】VisualStudioでのコンソールアプリのユニットテストの方法

Visual StudioでC#のコンソールアプリケーションのユニットテストを行う方法は、以下のステップに従って実行できます。Visual Studioには、ユニットテストをサポートするための統合フレームワークが用意されており、これを使用してコードの品質を保証し、バグの早期発見に役立てることができます。

ユニットテストはなぜ必要?

プログラムが大きくなるにつれて、それが正しく動くかどうかを確認することが難しくなります。ここで「ユニットテスト」というものが役立ちます。ユニットテストは、プログラムの一部分、つまり「ユニット」が期待通りに動くかどうかを確認するテストです。

例えば、電卓プログラムを作っているとしましょう。この電卓は足し算、引き算、掛け算、割り算ができます。ユニットテストでは、それぞれの機能(足し算、引き算、掛け算、割り算)が正しく動くかを一つずつ確認します。足し算のテストでは、2つの数を足した結果が正しいかを確認し、引き算のテストでは、2つの数を引いた結果が正しいかを確認します。

ユニットテストフレームワークは、これらのテストを簡単に作成し、実行するためのツールです。このフレームワークを使うことで、プログラムの小さな部分が正しく動いていることを確認できるため、全体としてのプログラムが期待通りに動くことに自信を持つことができます。

プログラミングを学んでいるときにテストを書く習慣を身につけることは非常に重要です。なぜなら、テストを書くことで、あなたのコードが正しく動くことを確認できるだけでなく、将来コードを変更したり、新しい機能を追加したりするときに、その変更が他の部分に意図しない影響を与えていないかをすぐに確認できるからです。

ユニットテスト方法

最初にテストされるソリューションを作成しておきます
(ここでは、ソリューション名、プロジェクト名とも:UnitTestSample

1. ユニットテストプロジェクトの作成

すでに作成済みのプロジェクトに対してユニットテストプロジェクトを作成していきます
(ここでは、プロジェクト名をTestCodeとします)

  1. Visual Studioを開きます。
  2. 既存のソリューションにユニットテストプロジェクトを追加する場合は、ソリューションエクスプローラーでソリューションを右クリックし、「追加」>「新しいプロジェクト…」を選択します。
  3. 「新しいプロジェクトの追加」ダイアログで、「テスト」カテゴリを選択し、「MSTest テストプロジェクト(.NET Core)」または「MSTest テストプロジェクト(.NET Framework)」を選びます。.NET Coreまたは.NET 5/6/7/8などの新しいフレームワークを使用している場合は、.NET Coreバージョンを選択します。
  4. プロジェクトに名前を付け、適切な場所を選択して「作成」をクリックします。

2. テストメソッドの記述

  • テストメソッドは [TestMethod] 属性を使用してマークされます。
  • テストロジックは、アサーションを使用して結果を検証します。例えば、Assert.AreEqual(expected, actual); は、期待される値と実際の値が等しいことを検証します。

3.プロジェクトの参照を追加

テストされるプロジェクトと、ユニットテストプロジェクトは独立しています
ユニってテストプロジェクト側から、テストされるプロジェクトが参照できるようにします

4. ユニットテストの実行

  • テストを実行するには、テストエクスプローラーを使用します。Visual Studioのメニューから「テスト」>「テストエクスプローラーの表示」を選択します。
  • テストエクスプローラーで、実行したいテストを選択し、「テストの実行」をクリックします。
  • テストの結果は、テストエクスプローラーに表示されます。

注意点

  • ユニットテストでは、テスト対象のメソッドがパブリックである必要があります。プライベートメソッドをテストする場合は、リフレクションを使用するか、テスト対象のクラスで内部メソッドにアクセスするためのパブリックなラッパーメソッドを提供する必要があります。
  • テスト対象のコードに外部依存がある場合(例:データベースへのクエリ)、モックオブジェクトやスタブを使用して依存関係を模倣することを検討してください。

サンプルコード

VisualStudioでの作成後画面になります

MyMath myMath = new MyMath();

int result = myMath.Add(3, 2);
Console.WriteLine(result);

public class MyMath
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}

テストコードを作成した時のテンプレートです

[TestClass]
public class MyMathTests
{
    [TestMethod]
    public void TestMethod1()
    {
    }
}

テストしたいコードに合わせてテストコードを作成していきます

[TestClass]
public class MyMathTests
{
    [TestMethod]
    public void 整数の足し算テストコード()
    {
        // Arrange(準備)
        var myMath = new MyMath();
        int a = 3;
        int b = 2;
        int expected = 5;

        // Act(実行)
        int result = myMath.Add(a, b);

        // Assert(検証)
        Assert.AreEqual(expected, result, "Addメソッドは2つの数値を正しく加算していません");
    }
}

このコードは、単体テストの一例です。単体テストは、プログラムの個々の部分が正しく動作することを確認するために行われます。ここでのテストは、MyMath クラスの Add メソッドを対象にしています。このメソッドは、2つの整数を受け取り、その和を返す機能を持っています。このコードは、MSTest フレームワークを使用しており、Visual Studioで広く使われています。

  • [TestClass]: この属性は、クラスがテストクラスであることを示します。これにより、テストランナーはこのクラス内でテストメソッドを探すことができます。
  • [TestMethod]: この属性は、メソッドがテストメソッドであることを示します。テストランナーは、この属性が付いたメソッドを自動的にテストとして実行します。
  • 整数の足し算テストコード: これはテストメソッドの名前です。この名前はテストの意図を説明していますが、通常は英語で記述されます。このメソッドは、MyMath クラスの Add メソッドが正しく整数の加算を行うことをテストします。

テストは三つのフェーズに分かれています:

  1. Arrange(準備): テストに必要なオブジェクトや値を準備します。ここでは MyMath クラスのインスタンスを作成し、加算する2つの整数(ab)、および期待される結果(expected)を定義しています。
  2. Act(実行): 実際にテストを行うためのアクションを実行します。ここでは、Add メソッドに ab を渡して実行し、結果を result に格納しています。
  3. Assert(検証): Act フェーズの結果が期待通りであることを検証します。Assert.AreEqual メソッドは、expected(期待される結果)と result(実際の結果)が等しいかどうかをチェックします。等しくない場合は、テストは失敗とみなされ、エラーメッセージ("Addメソッドは2つの数値を正しく加算していません")が表示されます。

このテストメソッドを実行することで、MyMath クラスの Add メソッドが正しく機能しているかどうかを自動的に検証することができます。これにより、コードに変更を加えた際にも、この機能が依然として期待通りに動作することを確認できます。

複数のテストを実施したいときは?

次のコードをテストしてみます

[TestClass]
public class MyMathTests
{
    [DataTestMethod]
    [DataRow(3, 2, 5, DisplayName = "3と2を加算すると5になる")]
    [DataRow(-1, -1, -2, DisplayName = "-1と-1を加算すると-2になる")]
    [DataRow(0, 0, 0, DisplayName = "0と0を加算すると0になる")]
    [DataRow(int.MaxValue, 1, int.MinValue, DisplayName = "intの最大値と1を加算するとオーバーフローする")]
    public void 整数の足し算テストコード(int a, int b, int expected)
    {
        // Arrange(準備)
        var myMath = new MyMath();

        // Act(実行)
        int result = myMath.Add(a, b);

        // Assert(検証)
        Assert.AreEqual(expected, result, $"{a}と{b}を加算した結果は{expected}になるべきです");
    }
}

WinFormsアプリのユニットテストの特徴:

Windows Forms (WinForms) アプリケーションでのユニットテストは、基本的なコンセプトではC#コンソールアプリケーションと大きく変わりません。ただし、UI要素が絡むため、いくつか特有の考慮事項があります。

  1. UIコンポーネントのテスト: WinFormsアプリでは、UIコンポーネント(ボタン、テキストボックス等)の振る舞いをテストする場合があります。これらのコンポーネントは直接的なロジックテストとは異なり、ユーザーインタラクションのシミュレーションや状態の検証を伴う場合が多いです。
  2. モデル-ビュー-プレゼンター (MVP) やモデル-ビュー-ビューモデル (MVVM) といったパターンの利用: UIのテストを容易にするために、ビジネスロジックをUIから分離する設計パターンがよく用いられます。これにより、ビジネスロジックのみを独立してテストすることができます。
  3. 依存性の注入 (DI): UIコントロールとビジネスロジック間の疎結合を促進するために、依存性の注入を利用することが推奨されます。これにより、テスト中にモックオブジェクトやスタブを簡単に挿入でき、UI以外の部分のテストを行うことが可能になります。

ユニットテストの実施方法:

基本的なユニットテストの設定方法はコンソールアプリケーションと同じですが、WinFormsアプリケーションの場合、以下のようなアプローチを取ることが一般的です。

  • ビジネスロジックの分離: UIコンポーネントからビジネスロジックを分離し、ロジックのみをテストするようにします。これはMVPやMVVMなどのパターンを使用して達成できます。
  • モックオブジェクトの使用: 外部システムやデータベースへの呼び出しを含むロジックをテストする場合は、モックオブジェクトを使用してこれらの依存関係を模倣します。
  • UIテストの分離: UI自体のテストは、ユニットテストとは別に考えるべきです。UIテストには、ユーザーインタラクションを模倣するための専用ツール(例: Selenium、TestStack.White)が必要になる場合があります。

結論:

WinFormsアプリケーションのユニットテストでは、ビジネスロジックのテストに焦点を当て、UIとの分離を心がける必要があります。MVPやMVVMのようなパターンを利用することで、テスト可能なコードを書くことができ、依存性の注入を通じてテストの柔軟性とメンテナンス性を高めることができます。

参考

C#,VisualStudio

Posted by hidepon