C#のNUnitテスト入門!初心者でもわかる自動テストの書き方とデバッグ方法
生徒
「C#でプログラムを作った後、それが正しく動いているか不安になることがあります。効率的にチェックする方法はありますか?」
先生
「それには『ユニットテスト』という手法が最適です。C#ではNUnit(エヌユニット)という有名なツールを使って、プログラムの動作を自動で検証できるんですよ。」
生徒
「自動でチェックしてくれるなんて便利ですね!初心者でも簡単に書けるものなのでしょうか?」
先生
「はい、基本さえ覚えれば大丈夫です。今回はNUnitを使ったテストの書き方を、基礎から丁寧に解説していきますね。」
1. ユニットテストとNUnitとは?
プログラミングにおけるユニットテスト(単位テスト)とは、作成したプログラムの最小単位(関数やメソッドなど)が、意図した通りに動くかを確認する作業のことです。パソコンに詳しくない方向けに例えると、料理の途中で「味見」をするようなものです。完成してから「塩辛すぎる!」と気づくのではなく、少しずつ確認しながら進めることで、大きな失敗を防ぐことができます。
NUnit(エヌユニット)は、C#でこのユニットテストを自動的に実行するための「フレームワーク」と呼ばれる道具箱のようなものです。これを使うことで、人間が手動で何度も同じ操作をして確認する手間を省き、ボタン一つでプログラムの健康診断ができるようになります。これを「自動テスト」と呼びます。開発現場では、バグ(プログラムの不具合)を早期発見するために欠かせない技術となっています。
2. テスト環境の準備とプロジェクトの作成
まずは、C#のプログラムと、それをテストするためのプロジェクトを用意する必要があります。通常、Visual Studioなどの開発環境を使って、以下の二つを作成します。
- テスト対象のプロジェクト:実際に動かしたいメインのプログラム。
- テストプロジェクト(NUnit):メインのプログラムを検査するためのプログラム。
「テストプロジェクト」を作成する際は、テンプレートから「NUnit テスト プロジェクト」を選択するだけで、必要な設定が自動的に行われます。ここで重要な用語が依存関係(リファレンス)です。テストプロジェクトが、メインのプロジェクトの中身を知るために、二つのプロジェクトを「繋ぐ」設定が必要になります。これを行うことで、テスト専用のプログラムからメインの計算式などを呼び出せるようになります。
3. 最初のテストプログラムを書いてみよう
まずは、非常にシンプルな足し算を行うクラス(プログラムの部品)をテストしてみましょう。ここでは、数値を二つ足して結果を返すだけの簡単なプログラムを用意しました。このプログラムが「1足す1は2になる」ことを、NUnitで証明してみます。
// テスト対象のクラス
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
// NUnitを使ったテストクラス
using NUnit.Framework;
[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_1足す1が2になること()
{
// 準備(計算機を用意する)
var calc = new Calculator();
// 実行(実際に計算させる)
int result = calc.Add(1, 1);
// 検証(結果が2であることを確認する)
Assert.That(result, Is.EqualTo(2));
}
}
コードの中にある[TestFixture]や[Test]という記述は属性(アトリビュート)と呼ばれます。これは、コンピューターに対して「ここから下がテストの内容ですよ!」と教えるための目印です。これがないと、NUnitはどこをテストすればいいか迷ってしまいます。
4. Assert(アサート)の役割と重要性
テストコードの中で最も重要なのがAssert(アサート)という命令です。日本語では「断言する」や「主張する」という意味があります。プログラムの結果が、自分の予想と一致しているかを判定する審判のような存在です。
例えば、Assert.That(result, Is.EqualTo(2)); という文は、「結果(result)は、2と等しい(EqualTo)と断言する!」という意味になります。もし計算結果が3だった場合、この審判は「失敗!」と判定を下し、プログラムのどこかに間違いがあることを教えてくれます。この「予想と実際の結果を比べる」というプロセスこそが、デバッグ(不具合修正)の第一歩となります。
5. 文字列や条件のテスト方法
数値だけでなく、文字のやり取りについてもテストが可能です。例えば、名前を受け取って「こんにちは、〇〇さん」と挨拶するプログラムを考えてみましょう。文字列が正しく結合されているかを確認するテストコードを書くことで、名前の表示ミスを防ぐことができます。
public class Greeter
{
public string SayHello(string name)
{
return "こんにちは、" + name + "さん";
}
}
[TestFixture]
public class GreeterTests
{
[Test]
public void SayHello_名前に合わせた挨拶を返すこと()
{
var greeter = new Greeter();
string result = greeter.SayHello("田中");
// 文字列が正しいかチェック
Assert.That(result, Is.EqualTo("こんにちは、田中さん"));
}
}
このように、期待される文章が正確に出力されるかをテストすることで、画面に表示されるメッセージの変更時にも、以前の機能が壊れていないか(これを退行テストやリグレッションテストと言います)を確認できるようになります。
6. 複雑な条件判定のテスト(Boolean型)
次に、「〇〇以上の場合は合格」といった、YesかNoかで答えるプログラムのテストを見てみましょう。これをBoolean(ブーリアン)型のテストと呼びます。例えば、テストの点数が80点以上なら合格(true)、そうでなければ不合格(false)を返す仕組みです。
public class Judge
{
public bool IsPassed(int score)
{
return score >= 80;
}
}
[TestFixture]
public class JudgeTests
{
[Test]
public void IsPassed_80点以上なら合格となること()
{
var judge = new Judge();
// 85点の場合
bool result = judge.IsPassed(85);
// Trueであることを確認
Assert.That(result, Is.True);
}
[Test]
public void IsPassed_79点なら不合格となること()
{
var judge = new Judge();
// 79点の場合
bool result = judge.IsPassed(79);
// Falseであることを確認
Assert.That(result, Is.False);
}
}
境界値(ボーダーライン)である「80点」や、その一歩手前の「79点」でテストを行うことは非常に重要です。プログラムのミスは、こういったギリギリの境目で起こりやすいためです。これを境界値分析と呼び、テストを設計する際の大切な考え方です。
7. 例外が発生することをテストする
プログラムを作っていると、わざとエラー(例外)を発生させて処理を中断させたい場合があります。例えば、「0で割り算をしようとしたとき」や「空っぽのデータを読み込もうとしたとき」などです。NUnitでは、この「正しくエラーが起きること」さえもテストの対象にできます。
public class BankAccount
{
public void Withdraw(int amount)
{
if (amount < 0)
{
throw new System.ArgumentException("マイナスの金額は引き出せません");
}
}
}
[TestFixture]
public class BankAccountTests
{
[Test]
public void Withdraw_マイナス指定でエラーが発生すること()
{
var account = new BankAccount();
// エラー(例外)が発生することを検証
Assert.Throws<System.ArgumentException>(() => account.Withdraw(-100));
}
}
このコードにある Assert.Throws という部分は、「この処理を実行したらエラーが出るはずだ!」と予測して確認しています。もしエラーが出なかった場合、逆にテストは失敗となります。これは、不正な操作に対してプログラムがしっかりとガードを固めているかをチェックするために使われます。
8. テスト結果の見方とデバッグの手順
テストを書いたら、Visual Studioなどの「テストエクスプローラー」という画面で実行ボタンを押します。実行結果は、色で識別されることが一般的です。
- 緑色のチェックマーク:テスト成功。プログラムは予想通りに動いています。
- 赤色の×マーク:テスト失敗。期待した結果と実際の動きが違います。
テストが失敗したときは、なぜ失敗したのかを確認するデバッグ作業に入ります。NUnitは「何が期待されていたか」と「実際はどうだったか」を画面に詳しく表示してくれます。例えば、「2を期待していましたが、実際は3でした」といったメッセージです。これを見ることで、ソースコードの何行目に問題があるのかを素早く突き止めることができます。自力で一からバグを探すよりも、テストツールを使う方が何倍も効率的に作業を進めることができるのです。