※『先輩、私のコードがバグだらけです!〜TUnit と Moq で始めるユニットテスト入門〜』シリーズは、全 6 回を予定しています。
前回はこちら
登場人物
先輩 : Visual Studio のショートカットキーをピアノのように弾きこなす。「GUI でできることは GUI でやる」派。
後輩 : 新人開発者。黒い画面(コマンドプロンプト)を見ると動悸がする。Visual Studio の「デバックの開始」ボタンを押す瞬間が一番好き。
1. その前に……「混ぜるな危険」の鉄則
後輩:「よし、前回の話でテストの大事さはわかりました! さっそく今作っている『販売管理アプリ』のプロジェクトに、TUnit をインストールしちゃいますね!」
先輩:「ストップ!! ……危ない、それはラーメンの出前に 『味見用の小皿』まで入れて配達するようなものだぞ」
後輩:「えっ、どういうことですか?」
先輩:「理由は 2 つある。まず 1 つ目は、『ゴミが混ざるから』。お客さんが注文したのは『ラーメン(アプリ本体)』だよね? そこに開発者が使った小皿(テストコード)や洗剤(テスト用ライブラリ)が混入してたらどう思う?」
先輩:「だろ? テストコードは製品には含めちゃいけない。そして2つ目の理由がもっと怖い。『循環参照(じゅんかんさんしょう)』 の問題だ」
後輩:「じゅんかん……さんしょう? 難しそうな言葉が出ました」
先輩:「簡単に言うと、『調理場(アプリ)』が『検査室(テスト)』 に頼っちゃダメってことだ。もし同じプロジェクトに混ぜてしまうと、うっかり『テスト用の偽物の肉』を、本番のラーメンに入れてしまうかもしれない」
後輩:「あ! 本体のコードの中で if (テスト中なら) { ... } みたいに書いちゃうってことですか?」
先輩:「そう。それをやると、『検査室がないと料理が作れない』というおかしな依存関係(循環)ができあがって、コードがスパゲッティみたいに絡まって解けなくなるんだ」
後輩:「なるほど……。『料理』は『検査』を知らなくても作れるべきなんですね」
先輩:「その通り! 『テスト → アプリ』の一方通行を守るためにも、ソリューションの中で 『調理場(本体プロジェクト)』と『検査室(テストプロジェクト)』 は明確に部屋を分けるんだ。これはユニットテストの鉄則!」
2. 儀式(セットアップ)はマウスで優雅に
先輩:「というわけで、まずは 『テスト専用の新しいプロジェクト』 を作るぞ。Visual Studio ならマウスだけで完結する」
- ソリューションを右クリックして 「追加」>「新しいプロジェクト」 を選択
- テンプレートから 「コンソール アプリ (Console App)」 を選ぶ(C#用)
※TUnit はコンソールアプリとして作るのが基本なんだ。 - 名前は
MyApp.Testsみたいに、末尾に.Testsを付けるのが慣習だ
後輩:「できました! ソリューションの中に2つのプロジェクトが並びましたね」
先輩:「次に、このテストプロジェクトに TUnit を入れるぞ」
1. MyApp.Tests プロジェクトを右クリック
2. 「NuGet パッケージの管理」 を選択
3. 「参照」タブで検索窓に TUnit と入力
4. 出てきたリストから TUnit を選んで 「インストール」 ボタンをクリック
後輩:「(カチッカチッ……)あ、これならネットショッピング感覚で簡単ですね!」

先輩:「仕上げに、『一方通行の矢印』 を引くぞ。テストプロジェクトの 『依存関係』 を右クリックして、『プロジェクト参照の追加』 から、本体のアプリ(調理場)を選んでチェックを入れるんだ」
後輩:「これで『検査室』から『調理場』は見れるけど、逆は見れない状態ですね! 準備完了です!」
3. 便利な属性 [Test] と IntelliSense
先輩:「まずは簡単な足し算クラス Calculator をテストしてみよう。こう書いてみて」
using TUnit.Core; // 1. TUnitを呼び出す
public class CalculatorTests
{
[Test] // 2. ここに便利な属性!
public async Task Add_ValidParms_ReturnResult()
{
// 準備 (Arrange)
var calculator = new Calculator();
// 実行 (Act)
int result = calculator.Add(1, 2);
// 検証 (Assert)
await Assert.That(result).IsEqualTo(3);
}
}
後輩:「先輩、この [Test] ってやつ。……あれ? 書いたそばから色が付きました」

先輩:「そう、これが 便利な属性(Attribute) だ。Visual Studioの IntelliSense(インテリセンス) が TUnit を認識している証拠だね。これで『このメソッドはテストですよ』と宣言されたわけだ」
4. コンパイル中に動く「Source Generator」
後輩:「……あれっ!? 先輩、なんか変です!」
先輩:「どうした?」
後輩:「まだ『実行ボタン』を押してないのに、『テスト エクスプローラー』 の画面に、今書いた 足し算のテストAdd_ValidParms_ReturnResult が表示されてます! 今までのツールだと、一度ビルドして実行しないと認識しなかったのに……なんで!?」
先輩:「ふっふっふ。気づいたか。それが TUnit の最大の特徴、Source Generator(ソースジェネレーター) の力だ」
後輩:「ソース……ジェネレーター?」
先輩:「そうだな、もっとイメージしやすい例で説明しよう。『修学旅行のバスの点呼』 を思い出してごらん」
これまでのツール(xUnit / NUnit):
-
実行時リフレクション
これは 『出発直前の手動点呼』 だ。バスが出発するぞ(実行ボタン ON)となってから、先生がわざわざ座席を回って「佐藤くんいるかー? 鈴木さんいるかー?」と一人ひとり確認する。だから全員確認するまで時間がかかるし、たまに見落としもある。
先輩:「でも、TUnitのSource Generatorは、『駅の自動改札機』 なんだ」
TUnit:
-
コンパイル時 Source Generator
君がコードを書いて(駅に入って)改札を通った瞬間に、「ピッ! 入場!」 と自動的にシステムに登録される。だから、バスが出発する(実行ボタンを押す)頃には、もう乗客名簿は完成しているんだ。
後輩:「なるほど! 『よーし、今から探すぞ』じゃなくて、『書いた瞬間にもう登録済み』 なんですね。これが『コンパイル中に動く』ってことか……!」
先輩:「そう。裏側で勝手に『名簿(実行用コード)』を作っておいてくれるんだ。これが最新のコンパイラ技術の力だよ」
5. 検証の合言葉 Assert.That
先輩:「テストの中身で一番大事なのはここだ」
await Assert.That(result).IsEqualTo(3);
後輩:「Assert.That……直訳すると『〜であることを断言する』ですね」
先輩:「そう。Visual Studioなら、Assert.That(result). と打った瞬間に、IsEqualTo(等しい)とか IsNotNull(空じゃない)とか、使える検証メソッドがズラッと候補に出てくるだろう?」
後輩:「はい! 自分で覚えなくても、選ぶだけで書けちゃいますね。これなら英語が苦手な僕でも間違えなくて済みそう!」
6. テスト実行! 緑色のランプを灯せ
先輩:「じゃあ、いよいよ実行だ。テストエクスプローラーの 『すべてのテストを実行』 ボタン(緑色の再生マーク)を押してごらん」
後輩:「(ポチッ)……うわっ、一瞬で終わりました! テストの横に 緑色のチェックマーク が付きました!」
先輩:「おめでとう。さらにコードの上を見てごらん。メソッドの上に小さな文字で CodeLens(コードレンズ) が出ているはずだ」
後輩:「あ! テストメソッドの上に緑色のチェックマークが付いて 『Test Passed』 と出ました! コードの中に直接結果が出るんですね」
先輩:「これで君は、『変更しても壊れていないことを、Visual Studio を見るだけで確認できる』 ようになったんだ。もうリリースのたび、神に祈る必要はないぞ」
後輩:「すごい……! 守られてる感がすごいです! これでもうバグなんて怖くないですね!」
先輩:「その意気だ。この 『緑色のランプ』 を常に灯し続けることが、開発者の精神安定にとって一番の薬になるからな。これからどんどんテストを書いて、その安心感を味わっていこう」
後輩:「はい! テストコード、書くのが楽しくなってきました!」
(第3回へ続く)



