C#のテスト入門|MSTest・NUnit・xUnitの違いと単体テストの書き方を初心者向けに解説

はじめに

C#でアプリケーションを開発していると、「この処理は本当に正しく動いているのか」「修正したら別の機能が壊れていないか」と不安になる場面があります。そこで重要になるのがテストです。

C#のテスト、つまり「csharp test」を学ぶと、プログラムの動作を自動で確認できるようになります。特に単体テストは、メソッドやクラスなど小さな単位で処理の正しさを確認できるため、初心者が最初に学ぶべきテスト手法です。

C#の単体テストでは、主にMSTest、NUnit、xUnitという3つのテストフレームワークが使われます。どれもC#でテストを書くための仕組みですが、属性の書き方、アサーションの書き方、パラメータ化テストの方法などに違いがあります。

この記事では、C#のテストの基本から、MSTest・NUnit・xUnitの違い、単体テストの書き方、実行方法、初心者がつまずきやすいポイントまで、順番に解説します。

1. C#のテストとは?初心者が最初に押さえるべき基礎

C#のテストとは、作成したプログラムが期待どおりに動作するかを確認するための作業です。手動で画面を操作して確認する方法もありますが、単体テストのようにコードでテストを自動化すると、同じ確認を何度でも素早く実行できます。

たとえば、次のような計算クラスがあるとします。

C#
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}

このAddメソッドに対して、「1と2を渡したら3が返る」ことをテストコードで確認できます。これがC#における単体テストの基本です。

1-1. C#におけるテストの目的

C#におけるテストの目的は、コードが期待どおりに動くことを継続的に確認することです。

主な目的は次のとおりです。

コードの不具合を早く見つけること。修正によって既存機能が壊れていないか確認すること。仕様どおりに実装されているかを明確にすること。将来のリファクタリングを安心して行えるようにすること。

初心者のうちは「動かしてみて問題なければよい」と考えがちですが、開発規模が大きくなるほど手動確認だけでは限界があります。C#のテストを自動化しておくことで、修正のたびに同じ確認を繰り返せるようになります。

1-2. 単体テスト・結合テスト・E2Eテストの違い

C#のテストには、確認する範囲によっていくつかの種類があります。

単体テストは、メソッドやクラスなど小さな単位を対象にするテストです。たとえば、計算処理、文字列変換、入力値チェックなどを個別に確認します。

結合テストは、複数のクラスや機能を組み合わせたときに正しく動作するかを確認するテストです。たとえば、サービスクラスとリポジトリクラスを組み合わせて、データ取得や保存が期待どおりに行われるかを確認します。

E2Eテストは、ユーザーの操作に近い形でシステム全体を確認するテストです。Webアプリであれば、ログイン画面に入力してボタンを押し、結果画面が表示されるところまで確認するようなテストです。

初心者はまず単体テストから始めるのがおすすめです。単体テストは範囲が小さく、失敗原因を特定しやすいため、C#のテスト入門に適しています。

1-3. C#で単体テストが重要な理由

C#で単体テストが重要なのは、ビジネスロジックの正しさを早い段階で確認できるからです。

たとえば、消費税計算、割引計算、入力値検証、日付判定、権限チェックなどは、アプリケーションの品質に直結します。これらの処理を手動で毎回確認するのは大変ですが、単体テストを書いておけば、dotnet testやVisual Studioのテストエクスプローラーから簡単に実行できます。Microsoftの.NETテスト関連ドキュメントでも、.NET CLIのdotnet testを使ってテストプロジェクトの単体テストを実行できることが説明されています。Microsoft Learn+1

単体テストがあると、コードを変更したときに「以前は通っていたテストが失敗した」とすぐに気づけます。これは、バグの混入を早期に発見するうえで非常に重要です。

1-4. テストを書くメリットと開発現場での活用シーン

C#でテストを書くメリットは、単にバグを見つけることだけではありません。

テストコードは、処理の仕様を表すドキュメントの役割も果たします。たとえば、Add_1と2を渡すと3を返すというテスト名があれば、そのメソッドがどのような入力に対してどのような結果を返すべきかが分かります。

開発現場では、次のような場面でC#のテストが活用されます。

新機能を追加したとき。既存コードをリファクタリングするとき。不具合を修正したとき。プルリクエストをレビューするとき。CI/CDで自動ビルドと自動テストを実行するとき。

特にチーム開発では、テストがあることで他の開発者も安心してコードを変更できます。

2. C#の単体テストで使われる主要フレームワーク

C#の単体テストでは、MSTest、NUnit、xUnitがよく使われます。どれも.NETでテストを書くためのフレームワークですが、設計思想や書き方に違いがあります。

2-1. MSTestとは

MSTestは、Microsoftが提供するテストフレームワークです。Visual Studioとの相性がよく、C#初心者でも導入しやすいのが特徴です。

MSTestでは、テストクラスに[TestClass]、テストメソッドに[TestMethod]を付けます。アサーションにはAssert.AreEqualAssert.IsTrueなどを使います。MicrosoftのMSTestドキュメントでは、属性、アサーション、データドリブンテスト、ライフサイクル管理などの書き方が説明されています。Microsoft Learn

Visual Studioを使ってC#を学んでいる初心者にとって、MSTestは最初に触れやすい選択肢です。

2-2. NUnitとは

NUnitは、.NET向けの代表的な単体テストフレームワークの1つです。もともとJUnitの影響を受けて作られた歴史があり、柔軟なテスト記述ができる点が特徴です。NUnit公式サイトでも、NUnitは.NET言語向けの単体テストフレームワークであると説明されています。nunit.org

NUnitでは、テストクラスに[TestFixture]、テストメソッドに[Test]を付けます。アサーションはAssert.That(actual, Is.EqualTo(expected))のように、読みやすい制約モデルで書けます。

複雑なテストケースや、パラメータ化テストを分かりやすく書きたい場合に向いています。

2-3. xUnitとは

xUnitは、.NET向けのモダンなテストフレームワークとして広く使われています。ASP.NET Coreなどのプロジェクトでも採用されることが多く、シンプルな設計が特徴です。

xUnitでは、通常のテストに[Fact]、複数データで実行するテストに[Theory]を使います。MSTestやNUnitのように、テストクラス用の属性を必ず付ける必要がないため、コードが比較的すっきりします。

xUnit v3ではテストプロジェクトの実行方式やパッケージ名などに変更があり、公式の移行ガイドでもv2からv3への変更点が整理されています。新規プロジェクトで採用する場合は、利用するxUnitのバージョンに合わせてテンプレートやパッケージを確認することが大切です。xUnit.net

2-4. 3つのフレームワークに共通する基本機能

MSTest、NUnit、xUnitには、いずれもC#の単体テストに必要な基本機能がそろっています。

テストメソッドを定義する機能。期待値と実際の値を比較するアサーション機能。例外発生を確認する機能。複数の入力データで同じテストを実行するパラメータ化テスト機能。テスト実行前後に初期化や後片付けを行う機能。

そのため、初心者がC#のテストを学ぶ場合、どれを選んでも基本的な単体テストは書けます。重要なのは、1つのフレームワークを選び、まずは基本構文に慣れることです。

3. MSTest・NUnit・xUnitの違いを比較

MSTest、NUnit、xUnitはどれもC#のテストに使えますが、書き方には違いがあります。ここでは、初心者が特に迷いやすいポイントを比較します。

3-1. 属性の書き方の違い

テストフレームワークごとに、テストクラスやテストメソッドを示す属性が異なります。

MSTestでは、クラスに[TestClass]、メソッドに[TestMethod]を付けます。

C#
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_ReturnsSum()
{
}
}

NUnitでは、クラスに[TestFixture]、メソッドに[Test]を付けます。NUnitのTestFixtureは、テストやセットアップ、ティアダウンメソッドを含むクラスを示す属性です。NUnit Documentation

C#
[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_ReturnsSum()
{
}
}

xUnitでは、メソッドに[Fact]または[Theory]を付けます。

C#
public class CalculatorTests
{
[Fact]
public void Add_ReturnsSum()
{
}
}

3-2. アサーションの書き方の違い

アサーションとは、実際の結果が期待値と一致するかを確認する処理です。

MSTestでは次のように書きます。

C#
Assert.AreEqual(3, result);

NUnitでは次のように書きます。

C#
Assert.That(result, Is.EqualTo(3));

NUnitの公式ドキュメントでは、アサーションは単体テストの中心的な要素であり、Assertクラスの静的メソッドとして多くのアサーションが提供されていると説明されています。NUnit Documentation

xUnitでは次のように書きます。

C#
Assert.Equal(3, result);

どれも「期待値と実際の値を比較する」という目的は同じです。ただし、引数の順番やメソッド名が違うため、フレームワークを切り替えるときは注意しましょう。

3-3. テスト前後処理の違い

テストでは、実行前にデータを準備したり、実行後に後片付けをしたりすることがあります。

MSTestでは、[TestInitialize][TestCleanup]を使います。

C#
[TestInitialize]
public void Setup()
{
// テスト前処理
}

[TestCleanup]
public void Cleanup()
{
// テスト後処理
}

NUnitでは、[SetUp][TearDown]を使います。

C#
[SetUp]
public void Setup()
{
// テスト前処理
}

[TearDown]
public void Cleanup()
{
// テスト後処理
}

xUnitでは、コンストラクタをテスト前処理、IDisposable.Disposeをテスト後処理として使うことが一般的です。

C#
public class CalculatorTests : IDisposable
{
public CalculatorTests()
{
// テスト前処理
}

public void Dispose()
{
// テスト後処理
}
}

xUnitは、MSTestやNUnitとは異なり、専用のセットアップ属性を使わない設計になっています。

3-4. パラメータ化テストの違い

パラメータ化テストとは、同じテストロジックを複数の入力値で実行する方法です。

MSTestでは、[DataTestMethod][DataRow]を使います。MSTestのデータドリブンテストでは、DataRowDynamicDataなどを使って、同じテストを複数の入力で実行できます。Microsoft Learn

C#
[DataTestMethod]
[DataRow(1, 2, 3)]
[DataRow(5, 7, 12)]
public void Add_ReturnsSum(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.AreEqual(expected, result);
}

NUnitでは、[TestCase]を使います。

C#
[TestCase(1, 2, 3)]
[TestCase(5, 7, 12)]
public void Add_ReturnsSum(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.That(result, Is.EqualTo(expected));
}

xUnitでは、[Theory][InlineData]を使います。

C#
[Theory]
[InlineData(1, 2, 3)]
[InlineData(5, 7, 12)]
public void Add_ReturnsSum(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.Equal(expected, result);
}

3-5. Visual Studio・dotnet testとの相性

C#のテストは、Visual Studioのテストエクスプローラーから実行できます。また、.NET CLIを使う場合はdotnet testで実行できます。

dotnet testコマンドは、ソリューションやプロジェクトをビルドし、テストランナーを使ってテストを実行するコマンドです。MicrosoftのCLIドキュメントでは、dotnet testがVSTestまたはMicrosoft Testing Platformを使ってテストを実行することが説明されています。Microsoft Learn

MSTest、NUnit、xUnitはいずれも適切なテストアダプターやパッケージを追加すれば、Visual Studioやdotnet testから実行できます。

3-6. 初心者におすすめのフレームワークはどれか

初心者には、Visual Studio中心で学ぶならMSTestがおすすめです。Microsoft公式の情報が多く、テンプレートから始めやすいためです。

一方で、読みやすいアサーションや柔軟なテスト記述を重視するならNUnitもよい選択です。既存プロジェクトでNUnitが使われている場合は、そのままNUnitを学ぶのが自然です。

ASP.NET Coreやモダンな.NET開発に慣れていきたい場合は、xUnitもおすすめです。シンプルな書き方で、実務でもよく使われます。

最初に大切なのは、3つを同時に深く学ぼうとしないことです。まずは1つ選び、C#の単体テストの考え方に慣れましょう。

4. C#で単体テストを書く前の準備

C#で単体テストを書くには、テスト対象のプロジェクトと、テストコードを書くためのテストプロジェクトを分けて用意するのが一般的です。

4-1. テスト対象のC#プロジェクトを用意する

まずは、テスト対象となるC#プロジェクトを用意します。

例として、SampleAppというクラスライブラリを作成します。

Bash
dotnet new classlib -n SampleApp

次に、テスト対象のクラスを作成します。

C#
namespace SampleApp;

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

public int Divide(int a, int b)
{
if (b == 0)
{
throw new ArgumentException("0で割ることはできません。");
}

return a / b;
}
}

このCalculatorクラスを、後ほど単体テストで確認します。

4-2. テストプロジェクトを作成する

次に、テストプロジェクトを作成します。MSTestを使う場合は次のコマンドです。

Bash
dotnet new mstest -n SampleApp.Tests

NUnitを使う場合は次のように作成できます。

Bash
dotnet new nunit -n SampleApp.Tests

xUnitを使う場合は次のように作成できます。

Bash
dotnet new xunit -n SampleApp.Tests

プロジェクト名は、テスト対象プロジェクト名に.Testsを付けると分かりやすくなります。

4-3. テスト対象プロジェクトを参照に追加する

テストプロジェクトからテスト対象のクラスを使うには、プロジェクト参照を追加する必要があります。

Bash
dotnet add SampleApp.Tests reference SampleApp

ソリューションを使っている場合は、次のようにソリューションへ両方のプロジェクトを追加しておくと管理しやすくなります。

Bash
dotnet new sln -n SampleSolution
dotnet sln SampleSolution.sln add SampleApp
dotnet sln SampleSolution.sln add SampleApp.Tests

参照を追加しないと、テストコードからCalculatorクラスを呼び出せません。

4-4. NuGetパッケージを確認する

テストプロジェクトには、テストフレームワーク本体やテストアダプター、Microsoft.NET.Test.Sdkなどのパッケージが必要です。

テンプレートから作成した場合、多くは最初から必要なパッケージが追加されています。ただし、既存プロジェクトに後からテストを追加する場合は、.csprojを開いてパッケージ参照を確認しましょう。

MSTestなら、主にMSTest.TestFrameworkMSTest.TestAdapterを確認します。

NUnitなら、NUnitNUnit3TestAdapterを確認します。

xUnitなら、xunitxunit.runner.visualstudioなどを確認します。

4-5. Visual StudioとCLIでの実行環境の違い

Visual Studioでは、テストエクスプローラーからテストを一覧表示して実行できます。GUIで操作できるため、初心者にも分かりやすい方法です。

CLIでは、ターミナルから次のように実行します。

Bash
dotnet test

CLIは、WindowsだけでなくmacOSやLinuxでも使いやすく、CI/CDとの相性も良い方法です。Microsoftの.NETテストドキュメントでも、.NET CLIはクロスプラットフォームであり、CI/CDパイプラインでも利用できると説明されています。Microsoft Learn

5. C#の単体テストの基本的な書き方

C#の単体テストは、基本の型を覚えると書きやすくなります。ここでは、フレームワークに依存しない考え方を中心に解説します。

5-1. テストコードの基本構成

単体テストの基本構成は、次の3つです。

準備する。実行する。確認する。

たとえば、Calculator.Addをテストする場合は、次のような流れになります。

C#
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.Equal(3, result);

最初にテスト対象のインスタンスを作成し、次にメソッドを実行し、最後に結果が期待値と一致するか確認します。

5-2. Arrange・Act・Assertパターン

C#の単体テストでは、Arrange・Act・Assertパターンがよく使われます。

Arrangeは準備です。テスト対象のインスタンスや入力値を用意します。

Actは実行です。テストしたいメソッドを呼び出します。

Assertは検証です。結果が期待どおりか確認します。

C#
[Fact]
public void Add_1と2を渡すと3を返す()
{
// Arrange
var calculator = new Calculator();

// Act
var result = calculator.Add(1, 2);

// Assert
Assert.Equal(3, result);
}

この形にすると、テストコードの意図が読みやすくなります。

5-3. テストメソッドの命名ルール

テストメソッド名は、何を確認しているのか分かる名前にします。

よく使われる命名例は次のような形です。

C#
メソッド名_条件_期待結果

たとえば、次のような名前です。

C#
Add_1と2を渡すと3を返す
Divide_0で割ると例外を投げる
GetUser_存在しないIDならNullを返す

日本語のメソッド名を使うか英語にするかはプロジェクトの方針によります。実務では英語で統一することも多いですが、初心者の学習段階では日本語名にすると意図を理解しやすくなります。

5-4. 成功するテストのサンプル

xUnitで成功するテストの例を見てみましょう。

C#
using SampleApp;
using Xunit;

public class CalculatorTests
{
[Fact]
public void Add_1と2を渡すと3を返す()
{
// Arrange
var calculator = new Calculator();

// Act
var result = calculator.Add(1, 2);

// Assert
Assert.Equal(3, result);
}
}

Add(1, 2)の結果は3なので、このテストは成功します。

5-5. 失敗するテストの読み方

次のテストは失敗します。

C#
[Fact]
public void Add_1と2を渡すと4を返す()
{
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.Equal(4, result);
}

Add(1, 2)の実際の結果は3ですが、期待値に4を指定しているため失敗します。

テストが失敗したときは、まず次の点を確認します。

期待値が正しいか。実際の値がどうなっているか。テスト対象コードにバグがあるのか。テストコード側の期待値や前提が間違っているのか。

テスト失敗は悪いことではありません。むしろ、問題を早く見つけるための重要な情報です。

6. MSTestでC#の単体テストを書く方法

MSTestは、Visual Studioとの相性がよく、C#初心者でも始めやすいテストフレームワークです。

6-1. MSTestの基本構文

MSTestでは、Microsoft.VisualStudio.TestTools.UnitTesting名前空間を使います。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SampleApp;

namespace SampleApp.Tests;

[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_1と2を渡すと3を返す()
{
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.AreEqual(3, result);
}
}

MSTestでは、テストクラスに[TestClass]、テストメソッドに[TestMethod]を付けるのが基本です。

6-2. TestClassとTestMethodの使い方

[TestClass]は、このクラスがテストクラスであることを示します。[TestMethod]は、このメソッドがテストとして実行されることを示します。

C#
[TestClass]
public class UserServiceTests
{
[TestMethod]
public void IsAdult_20歳ならTrueを返す()
{
// テスト処理
}
}

属性を付け忘れると、Visual Studioやdotnet testでテストが検出されないことがあります。

6-3. Assert.AreEqualの使い方

Assert.AreEqualは、期待値と実際の値が等しいことを確認するアサーションです。

C#
Assert.AreEqual(expected, actual);

例を見てみましょう。

C#
[TestMethod]
public void Add_5と7を渡すと12を返す()
{
var calculator = new Calculator();

var result = calculator.Add(5, 7);

Assert.AreEqual(12, result);
}

MSTestのアサーションには、AssertStringAssertCollectionAssertなどがありますが、Microsoftのドキュメントでは、新しいコードでは検出しやすさと一貫性の観点からAssertクラスを優先することが推奨されています。Microsoft Learn

6-4. DataTestMethodで複数パターンをテストする

MSTestで複数パターンをテストする場合は、[DataTestMethod][DataRow]を使います。

C#
[DataTestMethod]
[DataRow(1, 2, 3)]
[DataRow(10, 20, 30)]
[DataRow(-1, 1, 0)]
public void Add_複数の入力で正しい合計を返す(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.AreEqual(expected, result);
}

同じ処理を入力値だけ変えて確認できるため、テストコードの重複を減らせます。

6-5. MSTestが向いているケース

MSTestは、次のようなケースに向いています。

Visual Studio中心で開発している場合。Microsoft公式のテストフレームワークを使いたい場合。C#初心者が単体テストを学び始める場合。既存プロジェクトですでにMSTestが使われている場合。

書き方が分かりやすく、Visual Studioのテンプレートから始めやすいため、C#のテスト入門に適した選択肢です。

7. NUnitでC#の単体テストを書く方法

NUnitは、柔軟なテスト記述と読みやすいアサーションが特徴のテストフレームワークです。

7-1. NUnitの基本構文

NUnitでは、NUnit.Framework名前空間を使います。

C#
using NUnit.Framework;
using SampleApp;

namespace SampleApp.Tests;

[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_1と2を渡すと3を返す()
{
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.That(result, Is.EqualTo(3));
}
}

NUnitでは、[TestFixture]でテストクラスを示し、[Test]でテストメソッドを示します。

7-2. TestFixtureとTestの使い方

[TestFixture]は、テストを含むクラスに付ける属性です。[Test]は、個別のテストメソッドに付けます。

C#
[TestFixture]
public class StringUtilityTests
{
[Test]
public void IsNullOrEmpty_空文字ならTrueを返す()
{
// テスト処理
}
}

近年のNUnitでは、単純なテストクラスでは[TestFixture]を省略できるケースもありますが、初心者のうちは明示的に付けておくと分かりやすいです。

7-3. Assert.Thatの使い方

NUnitの代表的なアサーションがAssert.Thatです。

C#
Assert.That(actual, Is.EqualTo(expected));

例を見てみましょう。

C#
[Test]
public void Add_5と7を渡すと12を返す()
{
var calculator = new Calculator();

var result = calculator.Add(5, 7);

Assert.That(result, Is.EqualTo(12));
}

Assert.Thatは、「実際の値が、期待する条件を満たしているか」を自然に表現できます。

たとえば、次のような書き方もできます。

C#
Assert.That(result, Is.GreaterThan(0));
Assert.That(name, Does.Contain("test"));
Assert.That(items, Has.Count.EqualTo(3));

読みやすいテストを書きたい場合、NUnitのアサーションは便利です。

7-4. TestCaseで複数パターンをテストする

NUnitで複数パターンをテストする場合は、[TestCase]を使います。

C#
[TestCase(1, 2, 3)]
[TestCase(10, 20, 30)]
[TestCase(-1, 1, 0)]
public void Add_複数の入力で正しい合計を返す(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.That(result, Is.EqualTo(expected));
}

TestCaseは書き方がシンプルで、初心者にも分かりやすいパラメータ化テストです。

7-5. NUnitが向いているケース

NUnitは、次のようなケースに向いています。

読みやすいアサーションを書きたい場合。複数パターンのテストを簡潔に書きたい場合。既存プロジェクトでNUnitが使われている場合。柔軟なテスト機能を使いたい場合。

MSTestよりも表現力のあるテストを書きたい場合に、NUnitは有力な選択肢です。

8. xUnitでC#の単体テストを書く方法

xUnitは、シンプルでモダンなC#のテストフレームワークです。

8-1. xUnitの基本構文

xUnitでは、Xunit名前空間を使います。

C#
using SampleApp;
using Xunit;

namespace SampleApp.Tests;

public class CalculatorTests
{
[Fact]
public void Add_1と2を渡すと3を返す()
{
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.Equal(3, result);
}
}

MSTestの[TestClass]やNUnitの[TestFixture]に相当する属性を、通常のテストクラスに必ず付ける必要はありません。

8-2. FactとTheoryの違い

xUnitでは、[Fact][Theory]を使い分けます。

[Fact]は、入力データを外から渡さない通常のテストに使います。

C#
[Fact]
public void Add_1と2を渡すと3を返す()
{
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.Equal(3, result);
}

[Theory]は、複数の入力データを使って同じテストを実行する場合に使います。

C#
[Theory]
[InlineData(1, 2, 3)]
[InlineData(10, 20, 30)]
public void Add_複数の入力で正しい合計を返す(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.Equal(expected, result);
}

1つの固定条件を確認するならFact、複数パターンを確認するならTheoryと覚えると分かりやすいです。

8-3. Assert.Equalの使い方

xUnitで値の一致を確認する場合は、Assert.Equalを使います。

C#
Assert.Equal(expected, actual);

例を見てみましょう。

C#
[Fact]
public void Add_5と7を渡すと12を返す()
{
var calculator = new Calculator();

var result = calculator.Add(5, 7);

Assert.Equal(12, result);
}

MSTestのAssert.AreEqualと似ていますが、xUnitではAssert.Equalという名前になります。

8-4. InlineDataで複数パターンをテストする

xUnitで複数データを指定する場合は、[InlineData]を使います。

C#
[Theory]
[InlineData(2, 2, 4)]
[InlineData(3, 5, 8)]
[InlineData(-2, 2, 0)]
public void Add_複数の入力で正しい合計を返す(int a, int b, int expected)
{
var calculator = new Calculator();

var result = calculator.Add(a, b);

Assert.Equal(expected, result);
}

InlineDataは、数値や文字列などシンプルなデータを渡すときに便利です。より複雑なデータを使う場合は、MemberDataClassDataを使うこともあります。

8-5. xUnitが向いているケース

xUnitは、次のようなケースに向いています。

シンプルな構文でテストを書きたい場合。ASP.NET Coreなどモダンな.NET開発で使いたい場合。テストクラスの属性を減らしてすっきり書きたい場合。既存プロジェクトでxUnitが使われている場合。

C#のテストに慣れてきたら、xUnitのシンプルさは大きなメリットになります。

9. C#のテストでよく使うアサーション

C#の単体テストでは、さまざまなアサーションを使います。ここでは、初心者が最初に覚えるべき基本的なアサーションを紹介します。

9-1. 値が等しいことを確認する

値が等しいことを確認するアサーションは、最もよく使います。

MSTestの場合です。

C#
Assert.AreEqual(3, result);

NUnitの場合です。

C#
Assert.That(result, Is.EqualTo(3));

xUnitの場合です。

C#
Assert.Equal(3, result);

計算結果、変換結果、戻り値などを確認するときに使います。

9-2. nullかどうかを確認する

値がnullかどうかを確認することもよくあります。

MSTestの場合です。

C#
Assert.IsNull(result);
Assert.IsNotNull(result);

NUnitの場合です。

C#
Assert.That(result, Is.Null);
Assert.That(result, Is.Not.Null);

xUnitの場合です。

C#
Assert.Null(result);
Assert.NotNull(result);

データ取得処理や検索処理のテストでよく使います。

9-3. 例外が発生することを確認する

不正な入力に対して例外が発生することを確認するテストも重要です。

MSTestの場合です。

C#
Assert.ThrowsException<ArgumentException>(() =>
{
calculator.Divide(10, 0);
});

NUnitの場合です。

C#
Assert.Throws<ArgumentException>(() =>
{
calculator.Divide(10, 0);
});

NUnitのAssert.Throwsは、コード片を実行して特定の例外が投げられることを検証するためのメソッドとして説明されています。NUnit Documentation

xUnitの場合です。

C#
Assert.Throws<ArgumentException>(() =>
{
calculator.Divide(10, 0);
});

例外処理のテストは、異常系の品質を高めるために欠かせません。

9-4. true・falseを確認する

真偽値を確認する場合は、truefalseを検証します。

MSTestの場合です。

C#
Assert.IsTrue(result);
Assert.IsFalse(result);

NUnitの場合です。

C#
Assert.That(result, Is.True);
Assert.That(result, Is.False);

xUnitの場合です。

C#
Assert.True(result);
Assert.False(result);

入力値検証や権限判定などでよく使います。

9-5. コレクションの件数や中身を確認する

リストや配列を返す処理では、件数や中身を確認します。

MSTestの場合です。

C#
Assert.AreEqual(3, items.Count);
CollectionAssert.Contains(items, "apple");

NUnitの場合です。

C#
Assert.That(items, Has.Count.EqualTo(3));
Assert.That(items, Does.Contain("apple"));

xUnitの場合です。

C#
Assert.Equal(3, items.Count);
Assert.Contains("apple", items);

コレクションのテストでは、順序が重要なのか、件数だけ確認すればよいのかを意識しましょう。

10. 実践的なC#テストの書き方

基本的な単体テストに慣れたら、例外処理、非同期処理、外部依存を含む処理など、より実践的なケースにも対応できるようにしましょう。

10-1. 例外処理をテストする

例外処理のテストでは、「どの条件で、どの例外が発生するべきか」を確認します。

C#
[Fact]
public void Divide_0で割るとArgumentExceptionを投げる()
{
var calculator = new Calculator();

Assert.Throws<ArgumentException>(() =>
{
calculator.Divide(10, 0);
});
}

例外メッセージまで確認したい場合は、戻り値として例外オブジェクトを受け取ります。

C#
[Fact]
public void Divide_0で割ると指定メッセージの例外を投げる()
{
var calculator = new Calculator();

var exception = Assert.Throws<ArgumentException>(() =>
{
calculator.Divide(10, 0);
});

Assert.Equal("0で割ることはできません。", exception.Message);
}

異常系のテストを書くことで、想定外の入力に対する安全性を確認できます。

10-2. async・awaitを使う非同期メソッドをテストする

C#では、非同期メソッドをasyncawaitで書くことが多くあります。非同期メソッドのテストでは、テストメソッドもasync Taskにします。

C#
public class UserService
{
public async Task<string> GetNameAsync(int id)
{
await Task.Delay(10);
return id == 1 ? "Taro" : "Unknown";
}
}

xUnitでのテスト例です。

C#
[Fact]
public async Task GetNameAsync_IDが1ならTaroを返す()
{
var service = new UserService();

var result = await service.GetNameAsync(1);

Assert.Equal("Taro", result);
}

非同期メソッドをテストするときは、ResultWait()を使うより、awaitで自然に待機する書き方がおすすめです。

10-3. privateメソッドは直接テストすべきか

初心者が迷いやすいのが、privateメソッドを直接テストすべきかどうかです。

基本的には、privateメソッドを直接テストする必要はありません。privateメソッドは内部実装であり、外部から見えるpublicメソッドを通じて結果を確認するのが一般的です。

たとえば、publicメソッドの中でprivateメソッドを使っている場合、publicメソッドのテストが通れば、内部処理も間接的に確認できます。

もしprivateメソッドを直接テストしたくなるほど複雑な場合は、その処理を別クラスのpublicメソッドとして切り出すことを検討しましょう。これは、テストしやすい設計にもつながります。

10-4. 外部依存を含む処理をテストする

実務のC#コードでは、データベース、API、ファイル、現在時刻など、外部環境に依存する処理が多くあります。

たとえば、次のような処理はそのままだと単体テストしにくいです。

C#
public class OrderService
{
public decimal GetTotalPrice()
{
// データベースから注文情報を取得する
// 合計金額を計算する
return 1000;
}
}

外部依存があると、テスト実行時にデータベースが必要になったり、ネットワーク状態に左右されたりします。

このような場合は、インターフェースを使って依存先を差し替えられるようにします。

C#
public interface IOrderRepository
{
decimal GetTotalPrice();
}

public class OrderService
{
private readonly IOrderRepository _repository;

public OrderService(IOrderRepository repository)
{
_repository = repository;
}

public decimal GetTotalPrice()
{
return _repository.GetTotalPrice();
}
}

この設計にすると、テスト時に本物のデータベースではなく、テスト用の偽物の実装を渡せます。

10-5. モックを使ったテストの考え方

モックとは、外部依存をテスト用に置き換える仕組みです。C#では、Moqなどのモックライブラリがよく使われます。

たとえば、IOrderRepositoryをモック化して、常に1000を返すようにできます。

C#
public class FakeOrderRepository : IOrderRepository
{
public decimal GetTotalPrice()
{
return 1000;
}
}

モックを使うと、データベースやAPIに接続せずに、ビジネスロジックだけをテストできます。

ただし、初心者のうちは最初からモックを多用しすぎる必要はありません。まずは外部依存の少ないメソッドから単体テストを書き、慣れてきたらモックを学ぶとよいでしょう。

11. C#のテストを実行する方法

C#のテストは、Visual StudioやCLIから実行できます。開発環境やチームの運用に合わせて使い分けましょう。

11-1. Visual Studioでテストを実行する

Visual Studioでは、テストエクスプローラーを使ってテストを実行できます。

テストプロジェクトを含むソリューションを開きます。メニューからテストエクスプローラーを表示します。検出されたテスト一覧から、実行したいテストを選びます。すべてのテストをまとめて実行することもできます。

テストが成功すると緑色、失敗すると赤色で表示されるため、初心者にも結果が分かりやすいです。

11-2. dotnet testコマンドで実行する

CLIでテストを実行する場合は、次のコマンドを使います。

Bash
dotnet test

ソリューションファイルがあるフォルダで実行すると、含まれるテストプロジェクトが実行されます。

特定のテストプロジェクトだけ実行する場合は、次のように指定します。

Bash
dotnet test SampleApp.Tests

dotnet testは、ローカル開発だけでなくCI/CDでもよく使われます。

11-3. 特定のテストだけ実行する

特定のテストだけ実行したい場合は、--filterオプションを使います。Microsoftのドキュメントでは、dotnet testでフィルター式を使って選択した単体テストを実行する方法が説明されています。Microsoft Learn

Bash
dotnet test --filter "FullyQualifiedName~CalculatorTests"

特定のメソッド名を含むテストだけ実行することもできます。

Bash
dotnet test --filter "Name~Add"

テスト数が増えてきたときに便利です。

11-4. テスト結果の見方

テスト結果では、次の点を確認します。

成功したテスト数。失敗したテスト数。スキップされたテスト数。失敗したテストの名前。期待値と実際の値。例外メッセージ。スタックトレース。

失敗したテストを見るときは、まず「どのテストが失敗したのか」を確認します。次に、期待値と実際の値の差を見ます。

たとえば、期待値が3で実際の値が4なら、計算ロジックやテストデータに問題がある可能性があります。

11-5. CI/CDで自動テストを実行する

CI/CDでは、GitHub Actions、Azure DevOps、GitLab CIなどを使って、コードがプッシュされたタイミングで自動的にテストを実行できます。

たとえば、CIで次のような処理を行います。

コードを取得する。.NET SDKをセットアップする。依存パッケージを復元する。ビルドする。dotnet testを実行する。

自動テストにより、問題のあるコードが本番環境に入る前に検出しやすくなります。

12. 初心者がC#テストでつまずきやすいポイント

C#のテストを始めたばかりの頃は、テストコードそのものよりも、環境設定や考え方でつまずくことがよくあります。

12-1. テスト対象プロジェクトを参照できない

よくあるのが、テスト対象プロジェクトを参照に追加していないケースです。

テストコードで次のようなエラーが出る場合があります。

型または名前空間の名前 'SampleApp' が見つかりません

この場合は、テストプロジェクトからテスト対象プロジェクトへの参照を追加します。

Bash
dotnet add SampleApp.Tests reference SampleApp

Visual Studioを使っている場合は、テストプロジェクトの「依存関係」または「参照」からプロジェクト参照を追加できます。

12-2. テストが検出されない

テストが検出されない場合は、次の点を確認します。

テストメソッドに正しい属性が付いているか。テストクラスがpublicになっているか。テストメソッドが実行可能なシグネチャになっているか。必要なテストアダプターが入っているか。プロジェクトが正しくビルドできているか。

MSTestなら[TestClass][TestMethod]、NUnitなら[TestFixture][Test]、xUnitなら[Fact]または[Theory]を確認しましょう。

12-3. Assertの使い方が分からない

初心者は、どのAssertを使えばよいか迷いやすいです。

まずは、次の5つを覚えると十分です。

等しいことを確認する。nullかどうかを確認する。例外が発生することを確認する。trueまたはfalseを確認する。コレクションの件数や中身を確認する。

最初からすべてのアサーションを覚える必要はありません。必要になったタイミングで少しずつ増やしていきましょう。

12-4. テストコードが複雑になりすぎる

テストコードが複雑になりすぎると、何を確認しているのか分からなくなります。

よくある原因は、1つのテストで複数のことを確認しすぎていることです。

C#
[Fact]
public void UserService_いろいろ確認する()
{
// ユーザー作成
// ユーザー更新
// ユーザー削除
// 権限確認
}

このようなテストは、失敗したときに原因を特定しにくくなります。

できるだけ、1つのテストでは1つの振る舞いだけを確認しましょう。

12-5. 何をテストすればよいか分からない

初心者が最も悩みやすいのが、「何をテストすればよいか」です。

最初は、次のような処理からテストするとよいでしょう。

計算処理。条件分岐。入力値チェック。例外が発生する処理。文字列変換。日付や金額の判定。ビジネスルールを含む処理。

逆に、単純なプロパティのgetter/setterや、フレームワークの動作そのものをテストする必要はあまりありません。

自分が書いたロジックのうち、間違えると困る部分からテストしましょう。

13. C#の単体テストを書くときのベストプラクティス

C#の単体テストは、ただ書けばよいわけではありません。読みやすく、壊れにくく、保守しやすいテストを書くことが大切です。

13-1. 1つのテストで1つのことだけ確認する

1つのテストでは、1つの振る舞いだけを確認しましょう。

悪い例です。

C#
[Fact]
public void Calculator_複数機能をまとめて確認する()
{
var calculator = new Calculator();

Assert.Equal(3, calculator.Add(1, 2));
Assert.Equal(2, calculator.Divide(10, 5));
}

このテストでは、足し算と割り算を同時に確認しています。どちらかが失敗したとき、テストの目的が曖昧になります。

よい例です。

C#
[Fact]
public void Add_1と2を渡すと3を返す()
{
var calculator = new Calculator();

var result = calculator.Add(1, 2);

Assert.Equal(3, result);
}

[Fact]
public void Divide_10を5で割ると2を返す()
{
var calculator = new Calculator();

var result = calculator.Divide(10, 5);

Assert.Equal(2, result);
}

テストを分けることで、失敗原因が分かりやすくなります。

13-2. テスト名で期待結果が分かるようにする

テスト名は、仕様を表す文章のように書くのが理想です。

C#
public void CalculateTotal_税率10パーセントなら税込価格を返す()

この名前を見れば、何を確認しているテストなのかが分かります。

逆に、次のような名前は避けましょう。

C#
public void Test1()
public void Check()
public void CalculatorTest()

テスト名が曖昧だと、失敗したときに内容を読み解く手間が増えます。

13-3. 外部環境に依存しないテストを書く

単体テストは、できるだけ外部環境に依存しないようにします。

データベースの状態に依存する。外部APIの応答に依存する。現在時刻に依存する。ファイルパスに依存する。実行順序に依存する。

このようなテストは、実行環境によって成功したり失敗したりする不安定なテストになりやすいです。

外部依存はインターフェースで切り出し、テスト時にはモックやフェイクに置き換えることを検討しましょう。

13-4. 読みやすく保守しやすいテストコードにする

テストコードも本番コードと同じように、読みやすさが重要です。

Arrange・Act・Assertを分ける。不要な処理を書かない。テストデータを分かりやすくする。共通化しすぎない。失敗時に原因が分かるようにする。

テストコードは、将来の自分やチームメンバーが何度も読むコードです。短期的に動けばよいのではなく、長く保守できる形を意識しましょう。

13-5. テストしやすい設計を意識する

テストしやすいコードは、設計としても良いコードであることが多いです。

1つのクラスの責務が小さい。外部依存がインターフェースで分離されている。副作用が少ない。入力と出力が分かりやすい。ビジネスロジックがUIやDB処理から分離されている。

逆に、テストしにくいコードは、責務が大きすぎたり、外部依存が密結合になっていたりすることがあります。

単体テストを書くことは、C#コードの設計を見直すきっかけにもなります。

14. C#テストに関するよくある質問

ここでは、C#のテストを学び始めた初心者が疑問に感じやすい内容をまとめます。

14-1. MSTest・NUnit・xUnitはどれを選べばいい?

初心者がVisual Studioで学ぶならMSTestが始めやすいです。Microsoft公式の情報が多く、テンプレートも使いやすいためです。

読みやすいアサーションや柔軟なテスト記述を重視するならNUnitがおすすめです。

シンプルでモダンな書き方を好むならxUnitがおすすめです。

実務では、既存プロジェクトで使われているフレームワークに合わせるのが基本です。新規学習なら、まずは1つを選んで単体テストの考え方を身につけましょう。

14-2. C#の単体テストはいつ書くべき?

理想的には、実装と同じタイミングで単体テストを書くのがおすすめです。

新しいメソッドを追加したとき。バグを修正したとき。仕様が複雑な処理を実装したとき。リファクタリング前に既存動作を保護したいとき。

テスト駆動開発のように、先にテストを書いてから実装する方法もあります。ただし、初心者はまず「実装後にテストを書く」形から始めても問題ありません。

14-3. テストコードにもリファクタリングは必要?

必要です。

テストコードも時間が経つと重複が増えたり、読みづらくなったりします。テストコードが分かりにくいと、失敗したときの原因調査に時間がかかります。

ただし、過度な共通化には注意しましょう。テストコードは多少重複していても、読みやすさを優先した方がよい場合があります。

14-4. テストカバレッジは何%を目指すべき?

テストカバレッジとは、コードのうちどの程度がテストで実行されたかを表す指標です。

ただし、カバレッジ100%を目指せば品質が高くなるとは限りません。重要なのは、ビジネス上重要な処理やバグが起きやすい処理がきちんとテストされていることです。

初心者は、まず重要なロジックに対してテストを書くことを優先しましょう。カバレッジは、テスト不足の箇所を見つけるための参考指標として使うのがおすすめです。

14-5. 初心者はまず何から始めればいい?

初心者は、次の順番で始めるのがおすすめです。

まず、簡単な計算クラスを作ります。次に、MSTest、NUnit、xUnitのどれか1つでテストプロジェクトを作ります。そして、Addメソッドのような単純な処理に対して、成功するテストを書きます。その後、例外処理やパラメータ化テストに進みます。

最初から難しいモックやCI/CDまで理解しようとしなくて大丈夫です。まずは「テストを書いて、実行して、成功・失敗を確認する」流れに慣れましょう。

まとめ

C#のテストは、プログラムが期待どおりに動作することを確認するための重要な技術です。特に単体テストは、メソッドやクラスなど小さな単位で動作を確認できるため、初心者が最初に学ぶべきテスト手法です。

C#の単体テストでは、MSTest、NUnit、xUnitがよく使われます。

MSTestは、Microsoft公式でVisual Studioとの相性がよく、初心者にも始めやすいフレームワークです。

NUnitは、Assert.ThatTestCaseなどを使って、読みやすく柔軟なテストを書けるフレームワークです。

xUnitは、FactTheoryを中心に、シンプルでモダンなテストを書けるフレームワークです。

C#で単体テストを書くときは、Arrange・Act・Assertパターンを意識し、1つのテストで1つのことだけを確認しましょう。また、テスト名で期待結果が分かるようにし、外部環境に依存しないテストを書くことも大切です。

最初は、計算処理や入力値チェックのような小さなロジックから始めるのがおすすめです。C#のテストに慣れてくると、リファクタリングや機能追加をより安心して行えるようになります。C# testの基本を身につけることは、品質の高い.NETアプリケーションを開発するための大きな一歩です。