C# Assertの使い方完全ガイド|Debug.AssertとテストAssertの違い・失敗時の挙動まで解説

はじめに

C#で開発していると、Assertという言葉を目にする場面は大きく分けて2つあります。1つは開発中の前提条件を確認するために使うDebug.Assert、もう1つは単体テストで期待した結果になっているかを検証するAssertです。

どちらも「条件が正しいかを確認する」という点では似ていますが、目的・使う場所・失敗時の挙動・Releaseビルドでの扱いが大きく異なります。

たとえば、次のようなコードはいずれもAssertを使っています。

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

前者は主にデバッグ中の前提確認、後者はテスト結果の合否判定に使います。この違いを理解しないまま使うと、「Assertが効かない」「テストで失敗しない」「本番環境の入力チェックになっていない」といった問題につながります。

この記事では、C# Assertの基本から、Debug.Assertの使い方、MSTestのAssert、NUnit・xUnitとの違い、失敗時の挙動、実践サンプルまで解説します。

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

1-1. Assertは「想定どおりか」を確認するための仕組み

Assertとは、プログラムの状態や処理結果が「想定どおりであること」を確認するための仕組みです。

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

C#
int age = GetAge();

Debug.Assert(age >= 0, "年齢が負の値になっています");

このコードでは、「年齢は0以上であるはず」という前提をDebug.Assertで確認しています。もしageが負の値になった場合、開発者に対して「想定外の状態が発生している」と知らせることができます。

単体テストでも考え方は同じです。

C#
int result = Calculator.Add(2, 3);

Assert.AreEqual(5, result);

この場合は、「2 + 3の結果は5であるはず」という期待値を検証しています。結果が5でなければ、テストは失敗します。

つまりAssertは、プログラムの中に「これは正しいはず」という確認ポイントを置くためのものです。

1-2. C#で使われるAssertは大きく2種類ある

C#でよく使われるAssertは、大きく次の2種類です。

1つ目は、System.Diagnostics.Debug.Assertです。これは主に開発中のデバッグ用途で使います。

C#
using System.Diagnostics;

Debug.Assert(value > 0, "valueは正の値である必要があります");

2つ目は、単体テストフレームワークが提供するAssertです。MSTest、NUnit、xUnitなどで使います。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

Assert.AreEqual(expected, actual);

どちらも「条件を確認する」ためのものですが、使う場面は異なります。

Debug.Assertは、開発中にプログラム内部の前提条件を確認するためのものです。一方、単体テストのAssertは、テストメソッドの中で処理結果が期待どおりかを判定するためのものです。

1-3. Debug.Assertと単体テストのAssertを混同しやすい理由

Debug.Assertと単体テストのAssertは、どちらも名前にAssertが付いているため混同されやすいです。

さらに、どちらも条件が正しいかどうかを確認するという意味では似ています。

C#
Debug.Assert(result == 10);
C#
Assert.IsTrue(result == 10);

見た目だけを見ると、どちらも「resultが10であることを確認している」ように見えます。

しかし、目的は違います。Debug.Assertは開発者がデバッグ中に異常な状態に気づくためのものです。単体テストのAssertは、テスト結果を失敗として記録し、テストランナーやCI/CDで検出するためのものです。

この違いを理解していないと、単体テストの代わりにDebug.Assertを書いてしまったり、本番環境の入力チェックにDebug.Assertを使ってしまったりします。

1-4. Assertを使う場面・使わない場面

Assertを使うべき場面は、主に「開発者が想定している前提」や「テストで確認したい期待値」を明確にしたいときです。

たとえば、次のような場面ではAssertが役立ちます。

C#
Debug.Assert(items != null, "itemsはnullでない前提です");
Debug.Assert(index >= 0, "indexは0以上である必要があります");

単体テストでは、次のように処理結果を確認します。

C#
Assert.AreEqual("山田", user.Name);
Assert.IsNotNull(user.Email);

一方で、Assertを使うべきではない場面もあります。

代表的なのは、本番処理の入力チェックです。

C#
// よくない例
Debug.Assert(request.UserId > 0);

ユーザー入力、外部APIからのレスポンス、ファイル内容、データベースの値などは、開発者の想定どおりであるとは限りません。そのため、実際の業務処理ではif文でチェックし、必要に応じて例外を投げたり、エラーメッセージを返したりするべきです。

C#
if (request.UserId <= 0)
{
throw new ArgumentException("UserIdは1以上である必要があります");
}

Assertは便利ですが、エラーハンドリングの代わりではありません。

2. Debug.Assertの使い方

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

Debug.Assertを使うには、System.Diagnostics名前空間を使用します。

C#
using System.Diagnostics;

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

C#
Debug.Assert(条件);

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

C#
int count = GetCount();

Debug.Assert(count >= 0);

条件がtrueであれば何も起きません。条件がfalseになると、Assert失敗として扱われます。

メッセージを付ける場合は、次のように書きます。

C#
Debug.Assert(count >= 0, "countが負の値です");

さらに詳細な情報を付けることもできます。

C#
Debug.Assert(count >= 0, "countが負の値です", $"count = {count}");

2-2. 条件がtrueのとき・falseのときの挙動

Debug.Assertは、指定した条件がtrueの場合、何もせずに処理を続行します。

C#
int value = 10;

Debug.Assert(value > 0, "valueは正の値である必要があります");

Console.WriteLine("処理を続行します");

この場合、value > 0trueなので、Assertは何も表示せず、次の処理に進みます。

一方、条件がfalseの場合は、Assert失敗として扱われます。

C#
int value = -1;

Debug.Assert(value > 0, "valueは正の値である必要があります");

Console.WriteLine("処理を続行します");

この場合、開発環境や実行環境によって、Visual Studioの出力ウィンドウにメッセージが表示されたり、Assertダイアログが表示されたり、デバッガで中断できたりします。

重要なのは、Debug.Assertは通常の例外とは違うという点です。条件がfalseになったからといって、必ずthrowされるわけではありません。挙動は実行環境や設定に依存します。

2-3. メッセージを指定して原因をわかりやすくする

Debug.Assertでは、メッセージを指定することで、失敗した原因をわかりやすくできます。

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

ただし、これだけでは情報が少ない場合があります。どの処理で、どの値が原因だったのかを含めると、調査しやすくなります。

C#
Debug.Assert(
user != null,
"ユーザー情報の取得に失敗しました",
$"userId = {userId}"
);

メッセージを書くときは、「何が期待されていたのか」「実際にどの値だったのか」がわかるようにすると効果的です。

悪い例は次のようなメッセージです。

C#
Debug.Assert(user != null, "エラー");

これでは、何が問題なのか判断できません。

よい例は次のようなメッセージです。

C#
Debug.Assert(user != null, $"ユーザーが取得できませんでした。userId = {userId}");

Assertは失敗した瞬間に原因を特定しやすくするためのものなので、メッセージはできるだけ具体的に書くのがポイントです。

2-4. Debug.Assertが表示されない・効かないときの確認ポイント

Debug.Assertを書いたのに何も表示されない場合は、いくつか確認すべきポイントがあります。

まず、Debugビルドで実行しているかを確認します。Debug.Assertは通常、DEBUGシンボルが定義されている場合に有効です。Releaseビルドでは呼び出し自体が無効になることがあります。

次に、条件が本当にfalseになっているかを確認します。

C#
Debug.Assert(value > 0);

この場合、valueが1以上であれば何も起きません。Assertが「効いていない」のではなく、条件が成功しているだけです。

また、Visual Studioでデバッグ実行しているかどうかも関係します。出力ウィンドウにメッセージが出る場合もあれば、環境によっては目立った表示にならない場合もあります。

確認したい場合は、次のように確実に失敗するコードを一時的に書いてみるとよいでしょう。

C#
Debug.Assert(false, "Assertの動作確認");

これでも何も起きない場合は、ビルド構成、DEBUGシンボル、出力先、デバッグ実行の状態を確認します。

2-5. Debug.AssertはReleaseビルドでどうなるのか

Debug.Assertは、基本的にDebugビルドで開発中の問題を見つけるために使うものです。

Debug.Assertには条件付きコンパイルの仕組みが関係しており、DEBUGシンボルが定義されていないReleaseビルドでは、呼び出しが無効になることがあります。

たとえば、次のコードがあるとします。

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

Debugビルドでは、このAssertが評価されます。しかしReleaseビルドでは、Debug.Assertの呼び出しが実行対象から外れる可能性があります。

そのため、本番環境で必ずチェックしたい条件にDebug.Assertを使ってはいけません。

C#
// 本番処理のチェックとしては不適切
Debug.Assert(order.Amount > 0);

本番環境でも必要なチェックは、if文と例外、またはバリデーション処理として実装します。

C#
if (order.Amount <= 0)
{
throw new InvalidOperationException("注文金額は1以上である必要があります");
}

Debug.Assertはあくまで開発中の補助機能です。

3. 単体テストで使うAssertの使い方

3-1. MSTestのAssertとは

MSTestのAssertは、単体テストの結果が期待どおりかを検証するためのクラスです。

MSTestを使う場合、一般的には次の名前空間を使用します。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

テストクラスとテストメソッドは次のように書きます。

C#
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_2と3を渡すと5を返す()
{
var calculator = new Calculator();

int result = calculator.Add(2, 3);

Assert.AreEqual(5, result);
}
}

このテストでは、calculator.Add(2, 3)の結果が5であることを確認しています。resultが5ならテスト成功、5でなければテスト失敗です。

MSTestのAssertは、Visual Studioのテストエクスプローラーやdotnet testなどで実行され、成功・失敗がテスト結果として表示されます。

3-2. Assert.AreEqualで期待値と実際の値を比較する

Assert.AreEqualは、期待値と実際の値が等しいかを検証するメソッドです。

C#
Assert.AreEqual(期待値, 実際の値);

例を見てみましょう。

C#
[TestMethod]
public void Add_2と3を渡すと5を返す()
{
var calculator = new Calculator();

int actual = calculator.Add(2, 3);

Assert.AreEqual(5, actual);
}

この場合、期待値は5、実際の値はactualです。

Assert.AreEqualでは、基本的に第1引数に期待値、第2引数に実際の値を書きます。

C#
Assert.AreEqual(expected, actual);

順番を逆にしてもコンパイルは通ることがありますが、テスト失敗時のメッセージがわかりにくくなるため、期待値を先に書く習慣をつけましょう。

文字列の比較にも使えます。

C#
Assert.AreEqual("Hello", message);

オブジェクトの比較では、型によって比較方法が変わります。独自クラスの場合、参照が同じか、Equalsが適切に実装されているかに注意が必要です。

3-3. Assert.IsTrue/IsFalseで条件を検証する

Assert.IsTrueは、条件がtrueであることを検証します。

C#
Assert.IsTrue(result > 0);

Assert.IsFalseは、条件がfalseであることを検証します。

C#
Assert.IsFalse(user.IsDeleted);

例を見てみましょう。

C#
[TestMethod]
public void IsAdult_20歳ならtrueを返す()
{
var user = new User { Age = 20 };

bool result = user.IsAdult();

Assert.IsTrue(result);
}

条件が単純な場合はIsTrueIsFalseでも問題ありません。ただし、何を比較しているのかがわかりにくい場合は、より具体的なAssertを使ったほうがよいです。

たとえば、次のコードは失敗時に原因が少しわかりにくくなります。

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

この場合は、次のように書くほうが適切です。

C#
Assert.AreEqual(expected, actual);

具体的なAssertメソッドを使うと、テスト失敗時に期待値と実際の値が表示されやすくなります。

3-4. Assert.IsNull/IsNotNullでnullを検証する

Assert.IsNullは、対象がnullであることを検証します。

C#
Assert.IsNull(value);

Assert.IsNotNullは、対象がnullではないことを検証します。

C#
Assert.IsNotNull(value);

たとえば、存在しないユーザーを検索したときにnullが返ることを確認する場合は、次のように書きます。

C#
[TestMethod]
public void FindUser_存在しないIDならnullを返す()
{
var repository = new UserRepository();

var user = repository.FindUser(999);

Assert.IsNull(user);
}

逆に、存在するユーザーが取得できることを確認する場合は、次のように書きます。

C#
[TestMethod]
public void FindUser_存在するIDならユーザーを返す()
{
var repository = new UserRepository();

var user = repository.FindUser(1);

Assert.IsNotNull(user);
}

nullチェックでは、Assert.IsTrue(user != null)のように書くよりも、Assert.IsNotNull(user)と書いたほうが意図が明確です。

3-5. Assert.ThrowsExceptionで例外を検証する

Assert.ThrowsException<T>は、指定した例外が発生することを検証します。

C#
Assert.ThrowsException<ArgumentException>(() =>
{
// 例外が発生する処理
});

たとえば、引数が不正な場合にArgumentExceptionを投げるメソッドをテストする場合は、次のように書きます。

C#
[TestMethod]
public void Divide_0で割ろうとすると例外を投げる()
{
var calculator = new Calculator();

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

例外の内容まで確認したい場合は、戻り値として例外オブジェクトを受け取れます。

C#
[TestMethod]
public void CreateUser_名前が空なら例外を投げる()
{
var service = new UserService();

var ex = Assert.ThrowsException<ArgumentException>(() =>
{
service.CreateUser("");
});

Assert.AreEqual("名前は必須です", ex.Message);
}

例外が発生すること自体を確認するだけでなく、例外メッセージやプロパティを確認することで、より正確なテストになります。

3-6. CollectionAssert/StringAssertを使うケース

MSTestには、通常のAssert以外にも、コレクションや文字列を検証するための専用クラスがあります。

コレクションを検証する場合は、CollectionAssertを使います。

C#
CollectionAssert.AreEqual(expectedList, actualList);

例を見てみましょう。

C#
[TestMethod]
public void GetNumbers_1から3のリストを返す()
{
var expected = new List<int> { 1, 2, 3 };

var actual = NumberService.GetNumbers();

CollectionAssert.AreEqual(expected, actual);
}

文字列を検証する場合は、StringAssertを使えます。

C#
StringAssert.Contains(message, "エラー");

たとえば、エラーメッセージに特定の文字列が含まれることを確認する場合は次のように書きます。

C#
[TestMethod]
public void Validate_エラー時にメッセージを返す()
{
string message = Validator.Validate("");

StringAssert.Contains(message, "必須");
}

文字列やコレクションの検証では、Assert.IsTrueで無理に書くよりも、専用のAssertを使ったほうが意図が伝わりやすくなります。

4. Debug.AssertとテストAssertの違い

4-1. 目的の違い:デバッグ用かテスト判定用か

Debug.Assertと単体テストのAssertの最も大きな違いは、目的です。

Debug.Assertは、開発中に「この状態は本来ありえないはず」という前提を確認するために使います。

C#
Debug.Assert(order != null, "orderはnullでない前提です");

一方、単体テストのAssertは、テスト対象のメソッドが期待した結果を返すかを判定するために使います。

C#
Assert.AreEqual(1000, order.TotalPrice);

つまり、Debug.Assertは開発者向けの内部チェック、単体テストのAssertはテスト結果を判定するためのチェックです。

4-2. 実行タイミングの違い

Debug.Assertは、アプリケーションのコードが実行されるタイミングで評価されます。

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

// 処理
}

このProcessメソッドがDebugビルドで実行されたときに、Assertが評価されます。

一方、単体テストのAssertは、テストを実行したときに評価されます。

C#
[TestMethod]
public void Process_正常なユーザーなら処理できる()
{
var user = new User();

var result = service.Process(user);

Assert.IsTrue(result);
}

アプリケーションを普通に起動しただけでは、テストメソッド内のAssertは実行されません。Visual Studioのテストエクスプローラー、dotnet test、CI/CDなどでテストを実行したときに評価されます。

4-3. 失敗時の挙動の違い

Debug.Assertが失敗した場合、開発環境や設定に応じて、出力ウィンドウにメッセージが出たり、ダイアログが表示されたり、デバッガで中断できたりします。

ただし、通常の例外のように必ず処理が停止するとは限りません。

一方、単体テストのAssertが失敗すると、そのテストは失敗として扱われます。

C#
Assert.AreEqual(5, actual);

actualが5でなければ、テストメソッドは失敗し、テスト結果にエラーメッセージが表示されます。

テストAssertの失敗は、テストランナーに明確に伝わります。そのため、自動テストやCI/CDで品質を確認する用途に向いています。

4-4. Releaseビルドでの違い

Debug.Assertは、Releaseビルドでは無効になることがあります。これは、Debugクラスのメソッドが主にデバッグ用途であり、DEBUGシンボルに依存するためです。

そのため、Releaseビルドで必ず実行したいチェックにDebug.Assertを使ってはいけません。

一方、単体テストのAssertは、テストを実行すればRelease構成でも評価されます。

たとえば、次のテストはRelease構成でdotnet testを実行しても検証されます。

C#
Assert.AreEqual(expected, actual);

ただし、単体テストのAssertは本番アプリケーションの実行中に使うものではありません。テストプロジェクト内で使うものです。

4-5. CI/CDや自動テストで使えるかの違い

CI/CDで使うべきなのは、単体テストのAssertです。

GitHub Actions、Azure DevOps、GitLab CIなどでdotnet testを実行すると、テストの成功・失敗が結果として判定されます。

Bash
dotnet test

このとき、MSTest、NUnit、xUnitなどのAssertが失敗すると、ビルドやパイプラインを失敗として扱えます。

一方、Debug.AssertはCI/CDのテスト判定には向いていません。出力にメッセージが出ることはあっても、テストランナーが失敗として扱うとは限らないためです。

自動テストで検証したい内容は、必ずテストメソッド内のAssertで書きましょう。

4-6. どちらを使うべきか判断する基準

判断基準はシンプルです。

開発中に「この状態は本来ありえない」という内部前提を確認したいなら、Debug.Assertを使います。

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

処理結果が期待どおりかをテストとして残したいなら、単体テストのAssertを使います。

C#
Assert.AreEqual(expected, actual);
Assert.IsNotNull(result);

本番環境で必ずチェックしたいなら、Assertではなくif文やバリデーション、例外処理を使います。

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

つまり、使い分けは次のようになります。

用途使うもの
開発中の前提確認Debug.Assert
単体テストの合否判定テストフレームワークのAssert
本番処理の入力チェックif文、例外、バリデーション

5. Assert失敗時の挙動を詳しく理解する

5-1. Debug.Assertが失敗したときに起きること

Debug.Assertが失敗すると、開発環境に対してAssert失敗が通知されます。

C#
Debug.Assert(false, "ここでAssertが失敗します");

Visual Studioでデバッグ実行している場合、出力ウィンドウにメッセージが表示されたり、ダイアログが表示されたり、デバッガで中断できることがあります。

ただし、Debug.Assertは例外とは異なります。throwのように必ず例外が発生するわけではありません。

C#
Debug.Assert(false, "失敗");

Console.WriteLine("この行に進む可能性があります");

環境や選択によっては、Assert失敗後に処理を続行できます。

そのため、「失敗したら必ず処理を止めたい」という用途では、Debug.Assertではなく例外を使うべきです。

C#
if (value < 0)
{
throw new InvalidOperationException("valueが不正です");
}

5-2. MSTestのAssertが失敗したときに起きること

MSTestのAssertが失敗すると、そのテストメソッドは失敗として扱われます。

C#
[TestMethod]
public void Test()
{
int actual = 3;

Assert.AreEqual(5, actual);

Console.WriteLine("この行は通常実行されません");
}

Assert.AreEqual(5, actual)が失敗すると、そこでテストは失敗し、以降の処理は通常実行されません。

MSTestでは、Assert失敗時にテスト失敗用の例外が発生し、テストランナーがそれを検出します。Visual Studioのテストエクスプローラーでは、失敗したテスト、失敗メッセージ、スタックトレースなどを確認できます。

5-3. テスト結果に表示されるエラーメッセージの読み方

テストが失敗すると、テスト結果には期待値と実際の値が表示されます。

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

C#
Assert.AreEqual(5, actual);

actualが3だった場合、テスト結果には「期待値は5だが、実際は3だった」という内容のメッセージが表示されます。

このとき重要なのが、期待値と実際の値の順番です。

C#
Assert.AreEqual(expected, actual);

この順番で書いておけば、テスト結果を読んだときに原因を理解しやすくなります。

また、必要に応じてメッセージを追加できます。

C#
Assert.AreEqual(5, actual, "2 + 3 の計算結果が正しくありません");

メッセージを付けると、なぜその検証をしているのかがわかりやすくなります。

5-4. Assert失敗時に処理は継続されるのか

Debug.Assertの場合、失敗後に処理を継続できることがあります。これは、Debug.Assertがデバッグ用の通知であり、例外処理とは性質が異なるためです。

一方、単体テストのAssertが失敗した場合、そのテストメソッド内の後続処理は通常実行されません。

C#
Assert.AreEqual(1, actual1);
Assert.AreEqual(2, actual2);
Assert.AreEqual(3, actual3);

最初のAssertが失敗すると、2つ目以降のAssertは実行されないことが一般的です。

そのため、1つのテストメソッドに多くのAssertを詰め込みすぎると、最初の失敗しか確認できず、他の問題が見えにくくなることがあります。

複数の観点を検証したい場合は、テストメソッドを分けることを検討しましょう。

5-5. 失敗原因を特定しやすいメッセージの書き方

Assertのメッセージは、失敗したときに原因を特定するための手がかりになります。

悪い例は次のようなメッセージです。

C#
Assert.AreEqual(expected, actual, "失敗しました");

これでは、何が失敗したのかわかりません。

よい例は次のようなメッセージです。

C#
Assert.AreEqual(
expected,
actual,
$"税込価格の計算結果が不正です。price={price}, taxRate={taxRate}"
);

メッセージには、次の情報を含めると効果的です。

  • 何を検証しているのか

  • どの入力値で失敗したのか

  • 業務的に何が問題なのか

ただし、すべてのAssertに長いメッセージを書く必要はありません。Assert.AreEqualのように期待値と実際の値が明確に出る場合は、メソッド名やテスト名で十分なこともあります。

6. よく使うAssertメソッド一覧

6-1. 値の一致を確認するAssert

値の一致を確認する代表的なメソッドは、Assert.AreEqualです。

C#
Assert.AreEqual(expected, actual);

値が一致しないことを確認したい場合は、Assert.AreNotEqualを使います。

C#
Assert.AreNotEqual(unexpected, actual);

数値、文字列、列挙型などの比較でよく使います。

C#
Assert.AreEqual(10, actualCount);
Assert.AreEqual("Success", result.Status);
Assert.AreEqual(UserType.Admin, user.Type);

6-2. bool条件を確認するAssert

bool値や条件式を確認する場合は、Assert.IsTrueAssert.IsFalseを使います。

C#
Assert.IsTrue(user.IsActive);
Assert.IsFalse(user.IsDeleted);

ただし、値の一致を確認したい場合は、IsTrueではなくAreEqualを使ったほうがわかりやすいです。

C#
// あまりよくない
Assert.IsTrue(actual == expected);

// よい
Assert.AreEqual(expected, actual);

IsTrueIsFalseは、条件そのものがテストの主題である場合に使うとよいでしょう。

6-3. nullを確認するAssert

nullを確認する場合は、Assert.IsNullAssert.IsNotNullを使います。

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

nullでないことを確認したあとにプロパティを検証することもよくあります。

C#
Assert.IsNotNull(user);
Assert.AreEqual("山田", user.Name);

ただし、Assert.IsNotNullが失敗した場合、その後のuser.Nameには進まないため、NullReferenceExceptionを避けられます。

6-4. 型を確認するAssert

オブジェクトの型を確認したい場合は、MSTestではAssert.IsInstanceOfTypeを使います。

C#
Assert.IsInstanceOfType(result, typeof(AdminUser));

指定した型ではないことを確認する場合は、Assert.IsNotInstanceOfTypeを使います。

C#
Assert.IsNotInstanceOfType(result, typeof(GuestUser));

型の確認は、ファクトリメソッドやポリモーフィズムを使っている処理のテストで役立ちます。

C#
[TestMethod]
public void Create_AdminTypeならAdminUserを返す()
{
var user = UserFactory.Create("admin");

Assert.IsInstanceOfType(user, typeof(AdminUser));
}

6-5. 例外を確認するAssert

例外を確認する場合は、Assert.ThrowsException<T>を使います。

C#
Assert.ThrowsException<ArgumentException>(() =>
{
service.CreateUser("");
});

例外オブジェクトを受け取って、メッセージやプロパティを検証することもできます。

C#
var ex = Assert.ThrowsException<ArgumentException>(() =>
{
service.CreateUser("");
});

StringAssert.Contains(ex.Message, "名前");

例外が発生しないことを明示的に確認したい場合は、テスト対象の処理をそのまま実行し、例外が出なければテスト成功とすることが一般的です。

C#
[TestMethod]
public void CreateUser_正しい名前なら例外が発生しない()
{
var service = new UserService();

service.CreateUser("山田");
}

6-6. 文字列・コレクションを確認するAssert

文字列の一部を確認したい場合は、StringAssertを使います。

C#
StringAssert.Contains(message, "エラー");
StringAssert.StartsWith(message, "ERROR");
StringAssert.EndsWith(fileName, ".txt");

コレクションの内容を確認したい場合は、CollectionAssertを使います。

C#
CollectionAssert.AreEqual(expected, actual);
CollectionAssert.Contains(actual, item);
CollectionAssert.AllItemsAreNotNull(actual);

配列やリストの中身を比較する場合、単純なAssert.AreEqualでは参照比較になってしまうことがあります。そのため、要素の一致を確認したい場合はCollectionAssertを使うのが基本です。

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

7-1. MSTestのAssertの特徴

MSTestは、Microsoftが提供するテストフレームワークです。Visual Studioとの相性がよく、C#プロジェクトで標準的に使いやすいのが特徴です。

MSTestの基本的なAssertは次のように書きます。

C#
Assert.AreEqual(expected, actual);
Assert.IsTrue(condition);
Assert.IsNull(value);
Assert.ThrowsException<ArgumentException>(() => method());

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

C#
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_2と3を渡すと5を返す()
{
Assert.AreEqual(5, 2 + 3);
}
}

Visual Studioを中心に開発している場合や、Microsoft系の構成で統一したい場合に選ばれやすいフレームワークです。

7-2. NUnitのAssertの特徴

NUnitは、.NETで広く使われているテストフレームワークです。豊富なAssert機能を持ち、柔軟な書き方ができます。

NUnitでは、従来の書き方に加えて、制約ベースのAssert.Thatがよく使われます。

C#
Assert.That(actual, Is.EqualTo(expected));
Assert.That(value, Is.Not.Null);
Assert.That(list, Does.Contain(item));

例外の検証も次のように書けます。

C#
Assert.Throws<ArgumentException>(() =>
{
service.CreateUser("");
});

NUnitは表現力が高く、複雑な条件も読みやすく書けるのが特徴です。

7-3. xUnitのAssertの特徴

xUnitは、シンプルでモダンな設計のテストフレームワークです。ASP.NET Coreなどのプロジェクトでもよく使われます。

xUnitでは、テストメソッドに[Fact]を付けます。

C#
public class CalculatorTests
{
[Fact]
public void Add_2と3を渡すと5を返す()
{
Assert.Equal(5, 2 + 3);
}
}

xUnitのAssertは、MSTestやNUnitとメソッド名が少し異なります。

C#
Assert.Equal(expected, actual);
Assert.True(condition);
Assert.False(condition);
Assert.Null(value);
Assert.NotNull(value);
Assert.Throws<ArgumentException>(() => method());

MSTestのAssert.AreEqualに相当するものが、xUnitではAssert.Equalです。

7-4. Assert.AreEqualとAssert.Equalの違い

MSTestやNUnitでは、値の一致確認にAssert.AreEqualを使います。

C#
Assert.AreEqual(expected, actual);

一方、xUnitではAssert.Equalを使います。

C#
Assert.Equal(expected, actual);

どちらも期待値と実際の値を比較するためのものです。

ただし、フレームワークによって名前や細かい挙動、失敗時メッセージ、コレクション比較の扱いが異なります。

たとえば、MSTestではコレクション比較にCollectionAssertを使うことが多いです。

C#
CollectionAssert.AreEqual(expectedList, actualList);

xUnitでは、配列やコレクションに対してAssert.Equalを使う場面が多くあります。

C#
Assert.Equal(expectedList, actualList);

同じ「Assert」でも、使っているテストフレームワークに合わせて書き方を変える必要があります。

7-5. 既存プロジェクトではどのAssertを選ぶべきか

既存プロジェクトでは、基本的にすでに採用されているテストフレームワークに合わせるのが最も安全です。

MSTestのプロジェクトであれば、MSTestのAssertを使います。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

NUnitのプロジェクトであれば、NUnitのAssertを使います。

C#
using NUnit.Framework;

xUnitのプロジェクトであれば、xUnitのAssertを使います。

C#
using Xunit;

複数のテストフレームワークを混在させると、Assertクラスの名前が衝突したり、テスト実行環境が複雑になったりします。

特別な理由がなければ、1つのテストプロジェクトでは1つのフレームワークに統一しましょう。

8. Assertを使うときの注意点

8-1. 本番処理の入力チェックにDebug.Assertを使わない

Debug.Assertを本番処理の入力チェックに使うのは避けるべきです。

C#
// よくない例
public void Register(User user)
{
Debug.Assert(user != null);

Save(user);
}

このコードは、Debugビルドではusernullかどうかを確認できます。しかしReleaseビルドではDebug.Assertが実行されない可能性があります。

本番処理で必要なチェックは、次のように明示的に書きます。

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

Save(user);
}

ユーザー入力、APIリクエスト、ファイル読み込み結果など、外部から来る値は常に不正な可能性があります。Assertではなく、バリデーションや例外処理で対応しましょう。

8-2. テストでは具体的なAssertメソッドを選ぶ

単体テストでは、できるだけ具体的なAssertメソッドを選ぶことが重要です。

たとえば、次のような書き方は避けたほうがよいです。

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

この書き方でも検証はできますが、失敗時に期待値と実際の値がわかりにくくなります。

よりよい書き方は次のとおりです。

C#
Assert.AreEqual(expected, actual);

nullチェックなら、次のように書きます。

C#
Assert.IsNotNull(user);

文字列の一部を確認するなら、次のように書きます。

C#
StringAssert.Contains(message, "必須");

コレクションなら、次のように書きます。

C#
CollectionAssert.AreEqual(expected, actual);

具体的なAssertを使うことで、テストの意図が明確になり、失敗時の原因も追いやすくなります。

8-3. IsTrueばかり使うと失敗理由がわかりにくい

Assert.IsTrueは便利ですが、使いすぎるとテストが読みにくくなります。

C#
Assert.IsTrue(user.Name == "山田");
Assert.IsTrue(user.Age == 20);
Assert.IsTrue(user.Email.Contains("@"));

このように書くと、失敗時にどの値が期待と違ったのかがわかりにくくなります。

次のように、目的に合ったAssertを使いましょう。

C#
Assert.AreEqual("山田", user.Name);
Assert.AreEqual(20, user.Age);
StringAssert.Contains(user.Email, "@");

IsTrueは、条件そのものが意味を持つ場合に使うと効果的です。

C#
Assert.IsTrue(user.CanLogin());

単なる値比較であれば、AreEqualなどの具体的なメソッドを選びましょう。

8-4. 期待値と実際の値の順番を間違えない

多くのAssertメソッドでは、期待値を先に、実際の値を後に書きます。

C#
Assert.AreEqual(expected, actual);

たとえば、次のように書きます。

C#
int actual = calculator.Add(2, 3);

Assert.AreEqual(5, actual);

順番を逆にしても、テスト自体は動くことがあります。

C#
Assert.AreEqual(actual, 5);

しかし、失敗時のメッセージで「期待値」と「実際の値」が逆に表示され、原因調査の妨げになることがあります。

テストコードでは、常に次の順番を意識しましょう。

C#
Assert.AreEqual(期待値, 実際の値);

xUnitのAssert.Equalでも、基本的に同じ考え方です。

C#
Assert.Equal(expected, actual);

8-5. 1つのテストで検証しすぎない

1つのテストメソッドに多くのAssertを書きすぎると、テストの目的がわかりにくくなります。

C#
[TestMethod]
public void CreateUser_テスト()
{
var user = service.CreateUser("山田", 20);

Assert.IsNotNull(user);
Assert.AreEqual("山田", user.Name);
Assert.AreEqual(20, user.Age);
Assert.IsTrue(user.IsActive);
Assert.IsNotNull(user.CreatedAt);
}

この程度であれば問題ない場合もありますが、検証内容が増えすぎると、何を確認するテストなのかが不明確になります。

特に、異なる観点を1つのテストに詰め込むのは避けたほうがよいです。

たとえば、正常系、異常系、初期値、例外、権限チェックなどは別々のテストに分けると読みやすくなります。

C#
[TestMethod]
public void CreateUser_名前を設定する()
{
var user = service.CreateUser("山田", 20);

Assert.AreEqual("山田", user.Name);
}

[TestMethod]
public void CreateUser_作成直後は有効状態になる()
{
var user = service.CreateUser("山田", 20);

Assert.IsTrue(user.IsActive);
}

テストは、失敗したときに原因がすぐわかることが重要です。

9. C# Assertの実践サンプル

9-1. メソッドの戻り値をAssertで検証する

まずは、単純な計算メソッドをテストしてみます。

C#
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
}

MSTestでテストを書くと、次のようになります。

C#
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_2と3を渡すと5を返す()
{
var calculator = new Calculator();

int actual = calculator.Add(2, 3);

Assert.AreEqual(5, actual);
}
}

このテストでは、Add(2, 3)の戻り値が5であることを確認しています。

メソッドの戻り値を検証する場合は、Assert.AreEqualを使うのが基本です。

9-2. nullチェックをAssertで検証する

次に、ユーザー検索メソッドを例にします。

C#
public class UserRepository
{
public User? FindById(int id)
{
if (id == 1)
{
return new User { Id = 1, Name = "山田" };
}

return null;
}
}

存在するIDを指定した場合、ユーザーが返ることを確認します。

C#
[TestMethod]
public void FindById_存在するIDならユーザーを返す()
{
var repository = new UserRepository();

var user = repository.FindById(1);

Assert.IsNotNull(user);
Assert.AreEqual("山田", user.Name);
}

存在しないIDを指定した場合、nullが返ることを確認します。

C#
[TestMethod]
public void FindById_存在しないIDならnullを返す()
{
var repository = new UserRepository();

var user = repository.FindById(999);

Assert.IsNull(user);
}

nullを検証する場合は、Assert.IsNullAssert.IsNotNullを使うと意図が明確になります。

9-3. 例外が発生することをAssertで検証する

不正な引数が渡された場合に例外を投げるメソッドをテストします。

C#
public class UserService
{
public void CreateUser(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}

// 登録処理
}
}

テストコードは次のようになります。

C#
[TestMethod]
public void CreateUser_名前が空ならArgumentExceptionを投げる()
{
var service = new UserService();

var ex = Assert.ThrowsException<ArgumentException>(() =>
{
service.CreateUser("");
});

StringAssert.Contains(ex.Message, "名前は必須");
}

このテストでは、例外の型だけでなく、メッセージの内容も確認しています。

例外の検証では、Assert.ThrowsException<T>を使うことで、「例外が発生すること」をテストとして明確に表現できます。

9-4. リストや配列の中身をAssertで検証する

リストや配列の中身を検証する場合は、CollectionAssertを使います。

C#
public class NumberService
{
public List<int> GetNumbers()
{
return new List<int> { 1, 2, 3 };
}
}

テストコードは次のようになります。

C#
[TestMethod]
public void GetNumbers_1から3のリストを返す()
{
var service = new NumberService();
var expected = new List<int> { 1, 2, 3 };

var actual = service.GetNumbers();

CollectionAssert.AreEqual(expected, actual);
}

順序を含めて一致しているかを確認したい場合は、CollectionAssert.AreEqualが使えます。

特定の要素が含まれていることを確認する場合は、次のように書けます。

C#
CollectionAssert.Contains(actual, 2);

すべての要素がnullでないことを確認する場合は、次のように書けます。

C#
CollectionAssert.AllItemsAreNotNull(actual);

9-5. Debug.Assertを使って開発中の前提条件を確認する

Debug.Assertは、開発中に「ここではこの値が必ず存在するはず」という前提を確認するために使います。

C#
using System.Diagnostics;

public class OrderService
{
public void Process(Order order)
{
Debug.Assert(order != null, "orderがnullです");

// orderがnullでない前提で処理する
Debug.Assert(order.Items != null, "order.Itemsがnullです");
Debug.Assert(order.Items.Count > 0, "注文明細が空です");

// 処理
}
}

ただし、このコードを本番処理の安全性確保に使ってはいけません。orderが外部から渡される可能性があるなら、実際には次のようなチェックも必要です。

C#
public void Process(Order order)
{
if (order == null)
{
throw new ArgumentNullException(nameof(order));
}

if (order.Items == null || order.Items.Count == 0)
{
throw new InvalidOperationException("注文明細がありません");
}

Debug.Assert(order.Items.All(item => item.Quantity > 0), "数量が0以下の明細があります");

// 処理
}

このように、本番でも必要なチェックはif文で行い、開発中の前提確認としてDebug.Assertを補助的に使うのが適切です。

10. C# Assertでよくある疑問

10-1. Assertとif文は何が違うのか

Assertとif文は、どちらも条件を確認できます。しかし目的が違います。

if文は、実行時に起こりうる条件分岐やエラーハンドリングに使います。

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

一方、Assertは「本来は満たされているはずの条件」を確認するために使います。

C#
Debug.Assert(user != null, "この時点でuserはnullでない前提です");

単体テストのAssertは、処理結果が期待どおりかを検証するために使います。

C#
Assert.AreEqual("山田", user.Name);

本番で必要な制御にはif文、開発中の前提確認にはDebug.Assert、テストの合否判定にはテストAssertを使いましょう。

10-2. Debug.Assertは本番環境でも使えるのか

Debug.Assertは本番環境のチェック用途には向いていません。

ReleaseビルドではDebug.Assertの呼び出しが無効になることがあるため、本番環境で必ず実行される保証がありません。

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

C#
Debug.Assert(password.Length >= 8);

パスワード長のような本番でも必ず検証すべき内容は、バリデーションとして実装する必要があります。

C#
if (password.Length < 8)
{
throw new ArgumentException("パスワードは8文字以上である必要があります");
}

Debug.Assertは、あくまで開発中にバグを早期発見するための補助機能と考えましょう。

10-3. Assertで例外を発生させられるのか

単体テストでは、Assertの失敗によってテスト失敗用の例外が発生します。たとえば、MSTestのAssert.AreEqualが失敗すると、そのテストは失敗として扱われます。

C#
Assert.AreEqual(10, actual);

一方、Debug.Assertは通常のthrowとは異なります。条件がfalseになっても、必ず例外が投げられるわけではありません。

もしアプリケーションの処理として例外を発生させたいなら、明示的にthrowを書きます。

C#
if (value < 0)
{
throw new InvalidOperationException("valueは0以上である必要があります");
}

「テストで例外が発生することを確認したい」場合は、次のように書きます。

C#
Assert.ThrowsException<InvalidOperationException>(() =>
{
target.Execute();
});

10-4. Assertがコンパイルエラーになる原因は何か

Assertがコンパイルエラーになる原因として多いのは、名前空間の指定漏れです。

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

C#
using System.Diagnostics;

単体テストのMSTest Assertを使う場合は、次のusingが必要です。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

NUnitの場合は次のようになります。

C#
using NUnit.Framework;

xUnitの場合は次のようになります。

C#
using Xunit;

また、テストフレームワークのNuGetパッケージがインストールされていない場合も、Assertが見つからずコンパイルエラーになります。

MSTestであれば、一般的に次のようなパッケージを使用します。

XML
<PackageReference Include="MSTest.TestFramework" Version="..." />
<PackageReference Include="MSTest.TestAdapter" Version="..." />

プロジェクトテンプレートからテストプロジェクトを作成した場合は、通常これらが設定されています。

10-5. usingに指定する名前空間はどれか

使うAssertによって、指定する名前空間は異なります。

Debug.Assertを使う場合は、次の名前空間です。

C#
using System.Diagnostics;

MSTestのAssertを使う場合は、次の名前空間です。

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;

NUnitの場合は、次の名前空間です。

C#
using NUnit.Framework;

xUnitの場合は、次の名前空間です。

C#
using Xunit;

同じファイル内で複数のAssertが競合する場合は、完全修飾名を使うこともできます。

C#
System.Diagnostics.Debug.Assert(value > 0);

ただし、テストプロジェクト内で複数のテストフレームワークを混在させるのは避けたほうがよいです。

10-6. Visual StudioでAssertの失敗を確認する方法

Visual StudioでDebug.Assertの失敗を確認するには、Debugビルドでデバッグ実行します。

たとえば、次のコードを実行します。

C#
using System.Diagnostics;

Debug.Assert(false, "Assert失敗の確認");

デバッグ実行中であれば、出力ウィンドウにメッセージが表示されたり、環境によってはAssertの通知が表示されたりします。

単体テストのAssert失敗を確認する場合は、Visual Studioのテストエクスプローラーを使います。

テストメソッドを作成し、次のように失敗するAssertを書きます。

C#
[TestMethod]
public void 失敗するテスト()
{
Assert.AreEqual(1, 2);
}

テストを実行すると、テストエクスプローラーに失敗として表示されます。失敗したテストを選択すると、期待値、実際の値、失敗した行、スタックトレースなどを確認できます。

コマンドラインで確認する場合は、次のように実行します。

Bash
dotnet test

CI/CDでも同じようにdotnet testを実行することで、Assert失敗を検出できます。

まとめ

C#のAssertには、主にDebug.Assertと単体テストフレームワークのAssertがあります。

Debug.Assertは、開発中にプログラム内部の前提条件を確認するためのものです。Debugビルドでの不具合発見には役立ちますが、Releaseビルドでは無効になることがあるため、本番処理の入力チェックには使えません。

一方、MSTest、NUnit、xUnitなどの単体テストAssertは、テスト対象の処理が期待どおりに動作しているかを検証するために使います。失敗した場合はテスト失敗として記録され、Visual Studioやdotnet test、CI/CDで確認できます。

使い分けの基本は次のとおりです。

開発中の前提確認には、Debug.Assertを使います。

C#
Debug.Assert(user != null, "userはnullでない前提です");

テストの合否判定には、単体テストのAssertを使います。

C#
Assert.AreEqual(expected, actual);

本番環境で必ず必要なチェックには、if文や例外処理を使います。

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

Assertは、正しく使えばバグの早期発見やテスト品質の向上に役立ちます。重要なのは、Debug.AssertとテストAssertを混同せず、それぞれの目的に合った場所で使うことです。