C# Assertの使い方を基礎から解説|Debug.Assertとユニットテストの違い・よく使うメソッドまで

はじめに

C#で開発をしていると、Assertという言葉を目にする場面がよくあります。たとえば、デバッグ中に前提条件を確認するDebug.Assertや、ユニットテストで期待値と実際の結果を比較するAssert.AreEqualなどです。

ただし、同じ「Assert」という名前でも、使う場面や目的は大きく異なります。Debug.Assertは主に開発中の不具合検出に使い、ユニットテストのAssertはテストコードの成否を判定するために使います。

この記事では、C# Assertの基本から、Debug.Assertの使い方、ユニットテストにおけるAssert、MSTest・NUnit・xUnitの違い、よく使うメソッド、注意点までを基礎から解説します。

1. C#のAssertとは?まず押さえたい基本

1-1. Assertの役割:条件が正しいことを確認する仕組み

Assertとは、「この条件は正しいはずだ」とプログラム上で明示し、その条件が満たされているかを確認するための仕組みです。

たとえば、次のような前提があるとします。

C#
int count = GetItemCount();

// countは0以上であるはず
Debug.Assert(count >= 0);

このコードでは、countが0以上であることを前提にしています。もしcountがマイナスになった場合、それは想定外の状態であり、プログラムのどこかに問題がある可能性があります。

Assertを使うことで、こうした「本来起きないはずの状態」を早い段階で見つけやすくなります。

1-2. Assertが使われる主な場面

C#でAssertが使われる主な場面は、大きく分けて2つあります。

1つ目は、開発中のデバッグです。Debug.Assertを使い、変数の状態やメソッドの前提条件が正しいかを確認します。

2つ目は、ユニットテストです。MSTest、NUnit、xUnitなどのテストフレームワークで、処理結果が期待通りかどうかを検証します。

たとえば、ユニットテストでは次のように書きます。

C#
Assert.AreEqual(3, Calculator.Add(1, 2));

このコードは、Calculator.Add(1, 2)の結果が3であることを確認しています。

1-3. 例外処理やif文との違い

Assertは、例外処理やif文と似ているように見えるかもしれません。しかし、目的は異なります。

if文は通常の分岐処理に使います。

C#
if (user == null)
{
return;
}

例外処理は、実行時に発生しうる異常を扱うために使います。

C#
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}

一方、Assertは「この状態になるのはプログラム上おかしい」という開発者向けの確認に使います。

C#
Debug.Assert(user != null);

つまり、ユーザー入力の検証や本番環境で必要なエラーハンドリングには、Assertではなくif文や例外処理を使うべきです。Assertはあくまで、開発中にバグを早く見つけるための補助的な仕組みです。

1-4. C#で「Assert」と呼ばれる機能の種類

C#で「Assert」と呼ばれるものには、主に次の種類があります。

まず、System.Diagnostics.Debug.Assertです。これはアプリケーションコードの中で、デバッグ時に条件を確認するために使います。

次に、ユニットテストフレームワークのAssertです。MSTest、NUnit、xUnitなどで提供されており、テスト結果を検証するために使います。

たとえば、同じAssertでも次の2つは目的が違います。

C#
Debug.Assert(value != null);
C#
Assert.IsNotNull(value);

前者はデバッグ用、後者はテスト用です。C# Assertを理解するときは、まず「どのAssertの話をしているのか」を区別することが重要です。

2. Debug.Assertの使い方

2-1. Debug.Assertとは

Debug.Assertは、System.Diagnostics名前空間に含まれるデバッグ用のメソッドです。

指定した条件式がtrueであれば何も起こらず、falseであればデバッグ出力などを通じて問題を知らせます。

基本的には、開発中に「ここではこの条件が成り立っているはず」という前提を確認するために使います。

C#
using System.Diagnostics;

Debug.Assert(result != null);

2-2. Debug.Assertの基本構文

Debug.Assertの基本構文は次のとおりです。

C#
Debug.Assert(条件式);

たとえば、変数ageが0以上であることを確認する場合は、次のように書きます。

C#
int age = GetAge();

Debug.Assert(age >= 0);

条件式がtrueなら、そのまま処理が続きます。条件式がfalseなら、デバッグ時にAssert失敗として通知されます。

2-3. 条件式がfalseのときの動作

Debug.Assertの条件式がfalseになると、Assertが失敗します。

Visual Studioでデバッグ実行している場合は、出力ウィンドウにメッセージが表示されたり、環境によってはダイアログで通知されたりします。

C#
int value = -1;

Debug.Assert(value >= 0);

この例では、value >= 0falseになるため、Assertが失敗します。

重要なのは、Debug.Assertは通常の例外処理とは違うという点です。例外のように必ずcatchで処理するものではなく、開発者に「想定外の状態が起きている」と知らせるための仕組みです。

2-4. メッセージ付きで使う方法

Debug.Assertには、条件式だけでなくメッセージを付けることもできます。

C#
Debug.Assert(user != null, "userがnullです");

さらに詳細なメッセージを追加することもできます。

C#
Debug.Assert(
user != null,
"ユーザー情報が取得できませんでした",
"GetUserメソッドの戻り値を確認してください"
);

メッセージを付けておくと、Assertが失敗したときに原因を調査しやすくなります。特に、同じようなAssertが複数ある場合は、どの条件で失敗したのかを判断しやすくなります。

2-5. Debug.Assertが動作するビルド構成

Debug.Assertは、基本的にDEBUGシンボルが定義されているときに有効です。

通常、Visual StudioのDebugビルドではDEBUGが定義され、Releaseビルドでは定義されません。そのため、ReleaseビルドではDebug.Assertの呼び出し自体がコンパイル結果から除外されます。

C#
Debug.Assert(IsValidState());

Releaseビルドでは、この呼び出しが実行されないため、IsValidState()も呼ばれない点に注意が必要です。

そのため、Debug.Assertの条件式の中に副作用のある処理を書いてはいけません。

悪い例は次のようなコードです。

C#
Debug.Assert(UpdateState());

UpdateState()が状態を変更するメソッドだった場合、Debugビルドでは呼ばれますが、Releaseビルドでは呼ばれない可能性があります。これにより、DebugとReleaseで動作が変わってしまいます。

2-6. Debug.Assertを使うべきケース

Debug.Assertは、開発者が前提条件や内部状態を確認したいときに使います。

たとえば、次のようなケースです。

C#
Debug.Assert(items != null);
Debug.Assert(index >= 0);
Debug.Assert(index < items.Count);

これらは、プログラム内部のロジックとして「そうなっているはず」の条件です。

一方で、ユーザー入力や外部APIの結果など、実行時に不正な値が来る可能性があるものには、Debug.Assertだけで対応してはいけません。その場合は、if文や例外処理を使って、本番環境でも正しく処理できるようにする必要があります。

3. Debug.Assertの実践例

3-1. nullチェックに使う例

Debug.Assertは、nullでないことを確認する用途でよく使われます。

C#
using System.Diagnostics;

public void PrintUserName(User user)
{
Debug.Assert(user != null);

Console.WriteLine(user.Name);
}

この例では、PrintUserNameに渡されるuserがnullではないことを前提にしています。

ただし、外部から呼ばれる公開メソッドであれば、Debug.Assertだけでは不十分です。本番環境でもnullチェックが必要な場合は、次のように例外を投げる方が適切です。

C#
public void PrintUserName(User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}

Console.WriteLine(user.Name);
}

3-2. 引数の前提条件を確認する例

メソッドの内部で、引数が特定の条件を満たしていることを確認したい場合にもDebug.Assertが使えます。

C#
public decimal CalculateDiscount(decimal price, decimal discountRate)
{
Debug.Assert(price >= 0);
Debug.Assert(discountRate >= 0);
Debug.Assert(discountRate <= 1);

return price * discountRate;
}

この例では、価格が0以上であり、割引率が0から1の範囲にあることを確認しています。

ただし、このメソッドが外部から直接呼ばれる可能性があるなら、実際には次のように例外処理を入れるべきです。

C#
public decimal CalculateDiscount(decimal price, decimal discountRate)
{
if (price < 0)
{
throw new ArgumentOutOfRangeException(nameof(price));
}

if (discountRate < 0 || discountRate > 1)
{
throw new ArgumentOutOfRangeException(nameof(discountRate));
}

return price * discountRate;
}

Debug.Assertは「開発中に前提を確認するもの」であり、「不正な入力を防ぐ仕組み」ではないことを意識しましょう。

3-3. 想定外の分岐を検出する例

switch文などで、本来到達しないはずの分岐を検出する用途にも使えます。

C#
public string GetStatusText(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
return "処理待ち";

case OrderStatus.Completed:
return "完了";

case OrderStatus.Canceled:
return "キャンセル";

default:
Debug.Assert(false, $"想定外のステータスです: {status}");
return "不明";
}
}

この例では、定義済みのOrderStatus以外が渡された場合にAssertが失敗します。

ただし、defaultで何も返さないとコンパイルエラーになる場合があるため、必要に応じて戻り値や例外処理も用意します。

3-4. 配列やコレクションの状態を確認する例

配列やリストの件数、インデックス範囲を確認する場合にもDebug.Assertは便利です。

C#
public string GetFirstItem(List<string> items)
{
Debug.Assert(items != null);
Debug.Assert(items.Count > 0);

return items[0];
}

この例では、itemsがnullではなく、1件以上の要素を持っていることを前提にしています。

インデックスを使う場合は、範囲チェックにも使えます。

C#
public string GetItemAt(List<string> items, int index)
{
Debug.Assert(items != null);
Debug.Assert(index >= 0);
Debug.Assert(index < items.Count);

return items[index];
}

3-5. Debug.Assertを使うときの注意点

Debug.Assertを使うときは、次の点に注意しましょう。

まず、Releaseビルドでは実行されない可能性があります。そのため、本番環境で必要な入力チェックやエラーハンドリングをDebug.Assertに任せてはいけません。

次に、条件式に副作用のある処理を書かないことです。

C#
Debug.Assert(list.Remove(item));

このようなコードは避けるべきです。ReleaseビルドでRemoveが実行されない可能性があるためです。

正しくは、処理とAssertを分けます。

C#
bool removed = list.Remove(item);
Debug.Assert(removed);

また、Assertは多ければよいというものではありません。重要な前提や不変条件に絞って使うことで、コードの意図が分かりやすくなります。

4. ユニットテストにおけるAssertとは

4-1. ユニットテストで使うAssertの役割

ユニットテストにおけるAssertは、テスト対象の処理結果が期待通りかどうかを判定するために使います。

たとえば、足し算を行うメソッドをテストする場合、次のように書きます。

C#
[TestMethod]
public void Add_1と2を渡すと3を返す()
{
int result = Calculator.Add(1, 2);

Assert.AreEqual(3, result);
}

このテストでは、Calculator.Add(1, 2)の結果が3であれば成功し、そうでなければ失敗します。

4-2. Debug.AssertとユニットテストのAssertの違い

Debug.AssertとユニットテストのAssertは、どちらも条件を確認するために使いますが、目的が違います。

Debug.Assertは、アプリケーションコードの中で開発中の前提を確認するために使います。

一方、ユニットテストのAssertは、テストコードの中で期待結果を検証するために使います。

C#
Debug.Assert(user != null);
C#
Assert.IsNotNull(user);

前者はデバッグ時の補助、後者はテストの成否判定です。

また、ユニットテストのAssertはReleaseビルドでもテスト実行時に動作します。テストが失敗すれば、テストランナー上で失敗として表示されます。

4-3. MSTest・NUnit・xUnitにおけるAssertの位置づけ

C#のユニットテストでは、主に次のテストフレームワークが使われます。

MSTestは、Microsoftが提供するテストフレームワークです。Visual Studioとの相性がよく、Assert.AreEqualAssert.IsTrueなどのメソッドを使います。

NUnitは、長く使われているテストフレームワークで、柔軟なテスト記述ができます。クラシックなAssert.AreEqualのほか、Assert.Thatを使った書き方もよく使われます。

xUnitは、.NET向けのモダンなテストフレームワークです。Assert.EqualAssert.TrueAssert.Throwsなど、シンプルなメソッド名が特徴です。

4-4. テストコードでAssertを書く基本の流れ

テストコードでは、一般的に次の流れでAssertを書きます。

C#
[TestMethod]
public void Add_2つの数値を足し合わせる()
{
// Arrange
int a = 1;
int b = 2;

// Act
int result = Calculator.Add(a, b);

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

まず、テストに必要な値やオブジェクトを準備します。次に、テスト対象の処理を実行します。最後に、結果が期待通りかをAssertで確認します。

4-5. Arrange・Act・Assertパターンとは

Arrange・Act・Assertパターンは、ユニットテストを読みやすく整理するための考え方です。

Arrangeは、テストの準備です。入力値やモック、テスト対象オブジェクトを用意します。

Actは、実際にテスト対象の処理を実行する部分です。

Assertは、実行結果を検証する部分です。

このパターンに沿って書くと、テストコードの意図が分かりやすくなります。

C#
[Fact]
public void Add_ReturnsSum()
{
// Arrange
var calculator = new Calculator();

// Act
int actual = calculator.Add(1, 2);

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

5. よく使うAssertメソッド

5-1. Assert.AreEqual:値が等しいか確認する

Assert.AreEqualは、期待値と実際値が等しいかを確認するメソッドです。MSTestやNUnitでよく使われます。

C#
Assert.AreEqual(10, result);

一般的には、第1引数に期待値、第2引数に実際値を書きます。

C#
Assert.AreEqual(expected, actual);

値が一致すればテスト成功、一致しなければテスト失敗です。

5-2. Assert.AreNotEqual:値が異なるか確認する

Assert.AreNotEqualは、2つの値が異なることを確認します。

C#
Assert.AreNotEqual(0, result);

この例では、result0ではないことを検証しています。

等しくないこと自体が重要な仕様である場合に使います。

5-3. Assert.IsTrue/Assert.True:条件がtrueか確認する

条件式がtrueであることを確認したい場合は、MSTestやNUnitではAssert.IsTrue、xUnitではAssert.Trueを使います。

C#
Assert.IsTrue(user.IsActive);

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

C#
Assert.True(user.IsActive);

ただし、値の比較であれば、Assert.IsTrue(result == 10)よりもAssert.AreEqual(10, result)Assert.Equal(10, result)の方が、失敗時の原因を読み取りやすくなります。

5-4. Assert.IsFalse/Assert.False:条件がfalseか確認する

条件式がfalseであることを確認する場合は、Assert.IsFalseまたはAssert.Falseを使います。

C#
Assert.IsFalse(user.IsDeleted);

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

C#
Assert.False(user.IsDeleted);

フラグの状態を検証するときによく使います。

5-5. Assert.IsNull/Assert.Null:nullか確認する

値がnullであることを確認する場合は、MSTestやNUnitではAssert.IsNull、xUnitではAssert.Nullを使います。

C#
Assert.IsNull(result);

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

C#
Assert.Null(result);

検索結果が存在しないことや、初期状態で値が設定されていないことを確認する場合に使います。

5-6. Assert.IsNotNull/Assert.NotNull:nullでないか確認する

値がnullでないことを確認する場合は、Assert.IsNotNullまたはAssert.NotNullを使います。

C#
Assert.IsNotNull(user);

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

C#
Assert.NotNull(user);

オブジェクトが正しく生成されたか、検索結果が取得できたかなどを確認するときに使います。

5-7. Assert.ThrowsException/Assert.Throws:例外発生を確認する

例外が発生することを確認したい場合は、例外検証用のAssertを使います。

MSTestでは、Assert.ThrowsExceptionを使います。

C#
Assert.ThrowsException<ArgumentNullException>(() =>
{
service.CreateUser(null);
});

NUnitでは、Assert.Throwsを使います。

C#
Assert.Throws<ArgumentNullException>(() =>
{
service.CreateUser(null);
});

xUnitでも、Assert.Throwsを使います。

C#
Assert.Throws<ArgumentNullException>(() =>
{
service.CreateUser(null);
});

例外の種類まで確認することで、想定したエラー処理が行われているかをテストできます。

5-8. Assert.Fail:明示的にテストを失敗させる

Assert.Failは、明示的にテストを失敗させるメソッドです。

C#
Assert.Fail("ここに到達してはいけません");

たとえば、想定外の分岐に入った場合や、まだテストを実装中であることを示したい場合に使います。

C#
switch (status)
{
case OrderStatus.Completed:
Assert.AreEqual("完了", text);
break;

default:
Assert.Fail($"想定外のステータスです: {status}");
break;
}

ただし、Assert.Failを多用するとテストの意図が分かりにくくなるため、必要な場面に絞って使いましょう。

6. MSTest・NUnit・xUnitのAssertの違い

6-1. MSTestのAssertメソッド

MSTestでは、Microsoft.VisualStudio.TestTools.UnitTesting名前空間のAssertクラスを使います。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_ReturnsSum()
{
int result = Calculator.Add(1, 2);

Assert.AreEqual(3, result);
}
}

MSTestでは、[TestClass]でテストクラスを示し、[TestMethod]でテストメソッドを示します。

代表的なAssertメソッドには、次のようなものがあります。

C#
Assert.AreEqual(expected, actual);
Assert.AreNotEqual(notExpected, actual);
Assert.IsTrue(condition);
Assert.IsFalse(condition);
Assert.IsNull(value);
Assert.IsNotNull(value);
Assert.ThrowsException<ExceptionType>(() => { });
Assert.Fail();

6-2. NUnitのAssertメソッド

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

C#
using NUnit.Framework;

[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_ReturnsSum()
{
int result = Calculator.Add(1, 2);

Assert.AreEqual(3, result);
}
}

NUnitでは、Assert.Thatを使った書き方もよく使われます。

C#
Assert.That(result, Is.EqualTo(3));
Assert.That(user, Is.Not.Null);
Assert.That(items, Has.Count.EqualTo(3));

Assert.Thatを使うと、条件を自然な形で表現しやすくなります。

6-3. xUnitのAssertメソッド

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

C#
using Xunit;

public class CalculatorTests
{
[Fact]
public void Add_ReturnsSum()
{
int result = Calculator.Add(1, 2);

Assert.Equal(3, result);
}
}

xUnitでは、[Fact]で通常のテストメソッドを表します。複数の入力パターンをテストする場合は、[Theory][InlineData]を使います。

C#
[Theory]
[InlineData(1, 2, 3)]
[InlineData(2, 3, 5)]
public void Add_ReturnsSum(int a, int b, int expected)
{
int actual = Calculator.Add(a, b);

Assert.Equal(expected, actual);
}

xUnitのAssertは、AreEqualではなくEqualIsTrueではなくTrueのように、短い名前になっているのが特徴です。

6-4. 同じ確認内容を各フレームワークで書き比べる

同じ「1 + 2 の結果が3であること」を確認するテストを、MSTest・NUnit・xUnitで書き比べてみます。

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

C#
Assert.AreEqual(3, result);

NUnitの場合も、クラシックな書き方では次のように書けます。

C#
Assert.AreEqual(3, result);

NUnitのAssert.Thatを使う場合は次のようになります。

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

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

C#
Assert.Equal(3, result);

nullチェックの場合は次のようになります。

C#
// MSTest
Assert.IsNotNull(user);

// NUnit
Assert.IsNotNull(user);
// または
Assert.That(user, Is.Not.Null);

// xUnit
Assert.NotNull(user);

例外の確認は次のように書けます。

C#
// MSTest
Assert.ThrowsException<ArgumentNullException>(() => service.CreateUser(null));

// NUnit
Assert.Throws<ArgumentNullException>(() => service.CreateUser(null));

// xUnit
Assert.Throws<ArgumentNullException>(() => service.CreateUser(null));

6-5. どのテストフレームワークを選ぶべきか

MSTest、NUnit、xUnitのどれを選ぶべきかは、プロジェクトの方針やチームの慣れによって変わります。

Visual StudioやMicrosoft標準の構成に合わせたい場合は、MSTestが選びやすいです。既存プロジェクトでNUnitが使われている場合や、Assert.Thatの表現を好む場合はNUnitが適しています。

シンプルでモダンな書き方を好む場合や、.NETの新しいプロジェクトでよく使われる構成に合わせたい場合は、xUnitも有力な選択肢です。

初心者の場合は、学習している教材やチームで使っているフレームワークに合わせるのが最も実用的です。Assertの考え方自体は共通しているため、1つを理解すれば他のフレームワークにも応用できます。

7. C# Assertでよくあるエラーと対処法

7-1. Assertが見つからない場合

Assertが見つからない場合、名前空間の指定やテストフレームワークの導入が不足している可能性があります。

MSTestを使う場合は、次のusingが必要です。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

NUnitを使う場合は、次のusingが必要です。

C#
using NUnit.Framework;

xUnitを使う場合は、次のusingが必要です。

C#
using Xunit;

また、テストプロジェクトに必要なNuGetパッケージが追加されているかも確認しましょう。

Debug.Assertを使う場合は、次のusingが必要です。

C#
using System.Diagnostics;

テスト用のAssertDebug.Assertを混同している場合は、完全修飾名で書くと分かりやすくなります。

C#
System.Diagnostics.Debug.Assert(value != null);

7-2. AreEqualとEqualsの使い分けで迷う場合

Assert.AreEqualEqualsは似ていますが、役割が違います。

Equalsは、2つの値が等しいかどうかを判定するメソッドです。

C#
bool result = expected.Equals(actual);

一方、Assert.AreEqualは、テストの成否を判定するためのメソッドです。

C#
Assert.AreEqual(expected, actual);

テストコードでは、基本的にAssert.AreEqualを使う方が適切です。テストが失敗したときに、期待値と実際値が分かりやすく表示されるためです。

次のような書き方よりも、

C#
Assert.IsTrue(expected.Equals(actual));

次のように書く方が読みやすくなります。

C#
Assert.AreEqual(expected, actual);

xUnitの場合も同様に、次のように書きます。

C#
Assert.Equal(expected, actual);

7-3. 例外のテストがうまく書けない場合

例外のテストでは、「例外が発生する処理」をラムダ式で渡す必要があります。

悪い例は次のようなコードです。

C#
Assert.ThrowsException<ArgumentNullException>(
service.CreateUser(null)
);

この書き方では、Assert.ThrowsExceptionに渡す前にCreateUserが実行されてしまいます。

正しくは、次のようにラムダ式で包みます。

C#
Assert.ThrowsException<ArgumentNullException>(() =>
{
service.CreateUser(null);
});

xUnitの場合も同じ考え方です。

C#
Assert.Throws<ArgumentNullException>(() =>
{
service.CreateUser(null);
});

また、例外メッセージまで確認したい場合は、戻り値として取得した例外を検証します。

C#
var exception = Assert.ThrowsException<ArgumentNullException>(() =>
{
service.CreateUser(null);
});

Assert.AreEqual("user", exception.ParamName);

7-4. Debug.AssertがReleaseビルドで動かない場合

Debug.AssertがReleaseビルドで動かないのは、基本的には正常な動作です。

Debug.Assertはデバッグ用の仕組みであり、通常はDEBUGシンボルが定義されているときにだけ呼び出されます。

そのため、Releaseビルドでも必ず実行したいチェックには、Debug.Assertではなく、通常の条件分岐や例外処理を使います。

C#
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}

本番環境で必要な検証をDebug.Assertに任せている場合は、設計を見直しましょう。

7-5. テストが失敗した原因を読み解く方法

Assertでテストが失敗した場合は、まず期待値と実際値を確認します。

たとえば、次のようなテストがあるとします。

C#
Assert.AreEqual(100, total);

失敗した場合は、「期待していた値は100だが、実際には何だったのか」を確認します。

よくある原因には、次のようなものがあります。

期待値が間違っている場合があります。仕様を勘違いして、テストの期待値を誤って書いているケースです。

実装が間違っている場合もあります。計算ロジックや条件分岐にバグがあるケースです。

テストデータの準備が間違っている場合もあります。Arrangeの段階で必要なデータが不足していると、ActやAssertで想定と違う結果になります。

また、失敗時の情報を分かりやすくするために、比較用のAssertを適切に選ぶことも重要です。

C#
Assert.AreEqual(expected, actual);

このように書けば、期待値と実際値の違いが分かりやすくなります。

8. Assertを書くときのベストプラクティス

8-1. 1つのテストで何を検証するか明確にする

ユニットテストでは、1つのテストで何を検証しているのかを明確にすることが重要です。

悪い例は、1つのテストに多くの検証を詰め込みすぎることです。

C#
Assert.AreEqual("Taro", user.Name);
Assert.AreEqual(20, user.Age);
Assert.AreEqual("Tokyo", user.Address);
Assert.IsTrue(user.IsActive);

複数の項目を検証すること自体が悪いわけではありませんが、テストが失敗したときに原因が分かりにくくなる場合があります。

目的ごとにテストを分けると、どの仕様が壊れたのかを判断しやすくなります。

8-2. 期待値と実際値の順番に注意する

Assertでは、期待値と実際値の順番に注意しましょう。

多くのフレームワークでは、次の順番で書きます。

C#
Assert.AreEqual(expected, actual);

xUnitでも同様です。

C#
Assert.Equal(expected, actual);

順番を逆にしてもコンパイルは通ることがありますが、テスト失敗時のメッセージが分かりにくくなります。

C#
Assert.AreEqual(actual, expected); // 避けたい書き方

読みやすいテストを書くためにも、期待値を先、実際値を後に書く習慣をつけましょう。

8-3. テスト名で検証内容が分かるようにする

テスト名は、何を検証しているのかが分かる名前にしましょう。

たとえば、次のような名前は意図が分かりにくいです。

C#
public void Test1()

次のように書くと、テストの目的が分かりやすくなります。

C#
public void Add_1と2を渡すと3を返す()

英語で書く場合は、次のような形式もよく使われます。

C#
public void Add_WhenGiven1And2_Returns3()

テストが失敗したとき、テスト名を見るだけで何の仕様に問題があるのかを推測しやすくなります。

8-4. Assertメッセージを適切に使う

Assertには、失敗時のメッセージを指定できる場合があります。

C#
Assert.AreEqual(3, result, "1 + 2 の結果は3である必要があります");

メッセージを付けることで、失敗したときの原因を理解しやすくなります。

ただし、すべてのAssertに長いメッセージを書く必要はありません。テスト名や期待値・実際値から十分に分かる場合は、メッセージがなくても問題ありません。

重要なのは、失敗したときに調査しやすい情報が残ることです。

8-5. 過剰なAssertを避ける

Assertは便利ですが、過剰に書きすぎるとテストの意図がぼやけます。

たとえば、1つのテストで多くの仕様を同時に確認すると、どの仕様のテストなのか分かりにくくなります。

C#
Assert.IsNotNull(user);
Assert.AreEqual("Taro", user.Name);
Assert.AreEqual(20, user.Age);
Assert.IsTrue(user.IsActive);
Assert.AreEqual("Tokyo", user.Address);

このような場合は、「ユーザー名を確認するテスト」「年齢を確認するテスト」「有効状態を確認するテスト」のように分けた方が読みやすいことがあります。

ただし、同じオブジェクトの生成結果をまとめて確認した方が自然な場合もあります。大切なのは、テストの目的が明確であることです。

8-6. Debug.Assertを本番の入力検証に使わない

Debug.Assertは、本番環境の入力検証には使わないようにしましょう。

たとえば、次のようなコードは危険です。

C#
public void RegisterUser(User user)
{
Debug.Assert(user != null);

Save(user);
}

ReleaseビルドではDebug.Assertが実行されない可能性があるため、userがnullのままSaveに渡されるかもしれません。

本番環境でも必要な検証は、通常の条件分岐や例外処理で書きます。

C#
public void RegisterUser(User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}

Save(user);
}

そのうえで、内部状態の確認や開発中の前提確認としてDebug.Assertを補助的に使うのが適切です。

まとめ

C# Assertには、主にDebug.Assertとユニットテスト用のAssertがあります。

Debug.Assertは、開発中にプログラムの前提条件や内部状態を確認するための仕組みです。Debugビルドでは有効ですが、Releaseビルドでは実行されない可能性があるため、本番環境で必要な入力検証やエラーハンドリングには使わないようにしましょう。

一方、ユニットテストのAssertは、テスト対象の処理結果が期待通りかを確認するために使います。MSTestではAssert.AreEqual、NUnitではAssert.AreEqualAssert.That、xUnitではAssert.Equalなどを使います。

よく使うAssertメソッドには、値の一致を確認するAreEqualEqual、条件を確認するIsTrueTrue、nullを確認するIsNullNull、例外を確認するThrowsExceptionThrowsがあります。

C# Assertを正しく使うためには、Debug.AssertとユニットテストのAssertの違いを理解することが大切です。開発中の前提確認にはDebug.Assert、仕様の検証にはユニットテストのAssertを使い分けることで、バグを見つけやすく、保守しやすいコードを書けるようになります。