C#テスト入門|単体テストの書き方・実行方法とMSTest/xUnit/NUnitの選び方
はじめに
C#での開発を進める中で、「テストを書くこと」は品質を保つ上で欠かせない作業です。本記事では、C#の単体テストに焦点を当て、初心者でも理解しやすいように基礎知識から実践方法までを解説します。MSTest、xUnit、NUnitといった主要フレームワークの特徴や選び方も紹介し、効率的にテストを活用する方法をまとめました。
1. C#テストとは?検索ユーザーが最初に知りたい基礎知識
1-1. C#における「テスト」の意味とは
C#におけるテストとは、コードが正しく動作するかを確認するプロセスです。テストを行うことで、バグを早期に発見し、品質を維持できます。特に単体テストは、個々のメソッドやクラスの動作を確認することに重点を置きます。
1-2. 単体テスト・結合テスト・E2Eテストの違い
単体テスト:クラスやメソッド単位で動作を確認するテスト
結合テスト:複数のコンポーネントが正しく連携するかを確認
E2Eテスト(End-to-End):アプリケーション全体のフローを検証
単体テストは開発初期段階で効率よくバグを防ぐのに適しています。
1-3. なぜC#開発でテストを書くべきなのか
C#のようなオブジェクト指向言語では、複雑なロジックや依存関係が生まれやすく、テストを導入することで以下のメリットがあります。
バグを早期に発見できる
保守性の向上
リファクタリング時の安全性確保
チーム開発での品質統一
1-4. テストを書くことで防げる典型的なバグ
計算や文字列操作の誤り
null参照による例外
データベースやAPI呼び出しの不整合
条件分岐ロジックの漏れ
単体テストは、こうした問題をコードレベルで防ぐことができます。
2. C#の単体テストでできること・できないこと
2-1. 単体テストの対象になるコード
メソッド単位のロジック
計算処理や文字列操作
データ変換やフォーマット処理
2-2. 単体テストに向いていない処理
UI操作や画面描画
ネットワーク・ファイルI/O依存処理
データベース接続や外部APIのレスポンス
こうした処理はモックや統合テストで補うことが一般的です。
2-3. 良いテストコードと悪いテストコードの違い
良いテスト:簡潔、再現性が高い、読みやすい
悪いテスト:複雑で依存関係が多い、失敗しやすい、保守困難
2-4. テストしやすいC#コードの特徴
単一責任のクラス・メソッド
依存関係の注入が可能
副作用が少なく純粋な関数に近い
3. C#で単体テストを始めるための準備
3-1. 必要な開発環境
Visual Studio または Visual Studio Code
.NET SDK
テストフレームワーク(MSTest、xUnit、NUnit)
3-2. テストプロジェクトの作成方法
Visual Studioでは「新しいプロジェクト」→「単体テストプロジェクト」を選択します。CLIでは以下のコマンドを使用します。
Bashdotnet new mstest -n MyProject.Tests
3-3. テスト対象プロジェクトを参照する方法
テストプロジェクトから対象プロジェクトを参照することで、クラスやメソッドを呼び出してテスト可能になります。
Bashdotnet add reference ../MyProject/MyProject.csproj
3-4. NuGetパッケージとテストアダプターの役割
テストフレームワークパッケージ:Assertやテスト属性を提供
テストアダプター:IDEでテストを実行するための橋渡し
4. C#単体テストの基本的な書き方
4-1. テストメソッドの基本構造
C#[TestMethod]
public void Add_TwoNumbers_ReturnsSum()
{
// Arrange
var calculator = new Calculator();
// Act
var result = calculator.Add(2, 3);
// Assert
Assert.AreEqual(5, result);
}
4-2. Arrange・Act・Assertの考え方
Arrange:テスト対象の準備
Act:メソッドを実行
Assert:期待結果と比較
4-3. Assertで結果を検証する方法
Assert.AreEqual(expected, actual)Assert.IsTrue(condition)Assert.ThrowsException<ExceptionType>(action)
4-4. 正常系・異常系・境界値のテスト例
正常系:期待通り動作するケース
異常系:例外やエラーを検証
境界値:入力の端値での挙動を確認
4-5. テスト名の付け方と読みやすくするコツ
「メソッド名_条件_期待結果」の形式
意味が分かりやすく短くまとめる
他の開発者が見ても理解できるようにする
5. C#単体テストの実行方法
5-1. Visual Studioでテストを実行する方法
「テスト エクスプローラー」から実行
個別テストや全体テストを選択可能
5-2. .NET CLIでテストを実行する方法
Bashdotnet test
5-3. Visual Studio Codeでテストを実行する方法
「.NET Test Explorer」拡張機能を使用
CLIの
dotnet testも利用可能
5-4. テスト結果の見方
緑:成功
赤:失敗
黄色:スキップ
5-5. よくある実行エラーと対処法
参照エラー:プロジェクト参照を確認
NuGetパッケージ未導入:必要なパッケージを追加
属性の間違い:正しいテスト属性に修正
6. MSTest・xUnit・NUnitの違い
6-1. 3つのテストフレームワークの概要
MSTest:Microsoft純正、Visual Studioとの相性が良い
xUnit:最新設計で拡張性が高い
NUnit:歴史があり多機能で安定
6-2. MSTestの特徴・メリット・デメリット
特徴:属性が多く公式サポート
メリット:Visual Studioとの統合が容易
デメリット:拡張性はxUnitに劣る
6-3. xUnitの特徴・メリット・デメリット
特徴:柔軟でモダンな設計
メリット:データ駆動テストが簡単
デメリット:慣れるまで属性が少し分かりづらい
6-4. NUnitの特徴・メリット・デメリット
特徴:長く使われている安定フレームワーク
メリット:幅広いAssert機能、属性が豊富
デメリット:セットアップがやや複雑
6-5. 属性・Assert・テスト実行方法の比較
| フレームワーク | テスト属性 | 主なAssert | 実行方法 |
|---|---|---|---|
| MSTest | [TestMethod] | AreEqual, IsTrue | Visual Studio, CLI |
| xUnit | [Fact] | Equal, True | Visual Studio, CLI |
| NUnit | [Test] | AreEqual, IsTrue | Visual Studio, CLI |
6-6. パラメータ化テストの書き方の違い
MSTest:
[DataRow]xUnit:
[Theory]+[InlineData]NUnit:
[TestCase]
7. C#テストフレームワークの選び方
7-1. 初心者におすすめの選び方
Visual StudioユーザーならMSTest
モダンな書き方を学びたいならxUnit
7-2. Visual Studio中心の開発で選ぶ場合
MSTestは統合が簡単で手軽
CI/CDの構築も容易
7-3. チーム開発・CI/CDを重視する場合
xUnitやNUnitの拡張性が有利
データ駆動テストや依存関係注入がしやすい
7-4. 既存プロジェクトで選ぶ場合
既存フレームワークに合わせる
移行コストを考慮
7-5. 迷ったときの判断基準
開発環境との相性
CI/CDの対応状況
チームメンバーの経験
8. 実践例:C#で単体テストを書いてみる
8-1. テスト対象のサンプルコード
C#public class Calculator
{
public int Add(int a, int b) => a + b;
}
8-2. MSTestでテストを書く例
C#[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_ReturnsCorrectSum()
{
var calc = new Calculator();
var result = calc.Add(2, 3);
Assert.AreEqual(5, result);
}
}
8-3. xUnitでテストを書く例
C#public class CalculatorTests
{
[Fact]
public void Add_ReturnsCorrectSum()
{
var calc = new Calculator();
var result = calc.Add(2, 3);
Assert.Equal(5, result);
}
}
8-4. NUnitでテストを書く例
C#[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_ReturnsCorrectSum()
{
var calc = new Calculator();
Assert.AreEqual(5, calc.Add(2, 3));
}
}
8-5. 同じテストを3つのフレームワークで比較
基本構造はほぼ同じ
属性やAssertメソッドの書き方に差がある
CI/CDやチーム開発で選択基準となる
9. C#単体テストでよく使うテクニック
9-1. 例外が発生することをテストする
C#Assert.ThrowsException<InvalidOperationException>(() => method());
9-2. 非同期メソッドをテストする
C#[TestMethod]
public async Task AsyncMethod_ReturnsExpected()
{
var result = await AsyncMethod();
Assert.AreEqual(expected, result);
}
9-3. privateメソッドはテストすべきか
基本的にはpublicメソッドを通してテスト
privateはリファクタリングで影響を受けやすいため直接テストは避ける
9-4. DateTimeや乱数など変わる値を扱う方法
依存性注入で固定値を使う
モックやスタブを利用
9-5. モックを使って依存関係を切り離す方法
MoqやNSubstituteを使う外部サービスやDBへの依存を排除してテストの安定性を確保
10. C#テストでよくある失敗と解決策
10-1. テストが実行されない
属性が間違っている
プロジェクトの参照が不足している
10-2. 参照エラーが出る
対象プロジェクトの参照追加
NuGetパッケージの確認
10-3. Assertの書き方がわからない
フレームワークの公式Assertドキュメントを確認
基本的なEqual, True, Nullを理解する
10-4. テストが不安定になる
非同期処理や乱数を固定化
モックで外部依存を制御
10-5. テストコードの保守が大変になる
Arrange・Act・Assertの順序を守る
1テスト1目的を徹底
名前付け規則を統一
11. C#テストを実務で活かすためのベストプラクティス
11-1. 何をどこまでテストするべきか
ビジネスロジック中心にテスト
UIやライブラリは必要に応じて統合テスト
11-2. テストケースの優先順位の決め方
重要度が高い機能からテスト
バグ発生リスクが高い箇所を優先
11-3. テストコードを読みやすく保つ方法
コメントは最小限
名前付けを一貫
Arrange・Act・Assertを明確に
11-4. CI/CDで自動テストを実行する方法
GitHub ActionsやAzure DevOpsでdotnet testを実行
プルリクエスト時に自動検証
11-5. コードカバレッジとの付き合い方
高カバレッジは目安であり目的ではない
本当に重要なロジックを確実にテスト
まとめ
C#での単体テストは、コード品質を保つための強力な手段です。MSTest、xUnit、NUnitの特性を理解し、適切なフレームワークを選ぶことで、効率的にテストを実行できます。テストコードは単なる作業ではなく、リファクタリングやチーム開発の安全網として活用することが大切です。正しい書き方・実行方法・ベストプラクティスを身につけ、C#開発をより堅牢にしていきましょう。

