DIってテストでも使えるんですか先輩? ~依存性注入が「テストの味方」になる理由~

2025年4月25日金曜日

AIに聞いてみた C#

ChatGPTにDI(依存性注入)について聞いてみました(続き)。

タイトル、文章、イラスト: ChatGPT

登場人物

  • ミナト(左): 新人エンジニア。C#は書けるようになってきたけど、テストはまだ苦手。
  • ハルカ(右): 頼れる先輩。説明が丁寧で、コーヒー片手に教えてくれる系。

1. テストってやっぱりむずかしい

ミナト「はぁ~……テスト、また失敗しました……」

ハルカ「お、珍しく落ち込んでるじゃん。どしたの?」

ミナト「ユニットテストを書いてたんですけど、依存してるクラスのせいでうまくいかなくて……」

ハルカ「ふむふむ。ちなみにどんな感じのコード?」

C#:

  1. public class MyApp
  2. {
  3. private Logger _logger = new Logger();
  4.  
  5. public void Run() => _logger.Write("Hello");
  6. }

ミナト「このLoggerが原因で……テストで何が出力されるか確認できないんですよ」

ハルカ「あー、それね。newしちゃってる問題だね」

2. テストしにくいコードの問題点

ハルカ「MyAppが自分でLoggerを作っちゃってるでしょ?これが密結合ってやつ。変更しづらくて、テストもしにくい」

ミナト「じゃあ、どうすればいいんですか……?」

ハルカ「そこでDIの出番ってわけよ!」

ミナト「あっ! あのILoggerとかで渡すやつですね!」

3. DIを使った「テストしやすい」形に書き換え

C#:

  1. public interface ILogger
  2. {
  3. void Write(string message);
  4. }
  5.  
  6. public class ConsoleLogger : ILogger
  7. {
  8. public void Write(string message) => Console.WriteLine(message);
  9. }
  10.  
  11. public class MyApp
  12. {
  13. private readonly ILogger _logger;
  14.  
  15. public MyApp(ILogger logger)
  16. {
  17. _logger = logger;
  18. }
  19.  
  20. public void Run() => _logger.Write("Hello");
  21. }

ミナト「なるほど~! ILoggerをコンストラクタで受け取るようにすれば……」

ハルカ「そう! どんなILoggerを使うかは外の世界が決める。だからテストではモックを渡せばいいんだよ」

ミナト「モック……やってみます!」

4. モックでユニットテスト!

C#:

  1. public class MockLogger : ILogger
  2. {
  3. public string? LastMessage { get; private set; }
  4.  
  5. public void Write(string message)
  6. {
  7. LastMessage = message;
  8. }
  9. }
  10.  
  11. [TestMethod]
  12. public void Run_LogsHello()
  13. {
  14. var logger = new MockLogger();
  15. var app = new MyApp(logger);
  16.  
  17. app.Run();
  18.  
  19. Assert.AreEqual("Hello", logger.LastMessage);
  20. }

ミナト「あ! これ……ちゃんと中のWrite()が呼ばれたか確認できてる!」

ハルカ「そうそう。これがDI×テストの醍醐味。振る舞いを記録できるから、見えない部分も検証できる」

ミナト「何をしたかが見えるって、安心感ありますね」

5. ちょっと休憩: DIとテストの関係を図にすると……

ハルカ「本番用もテスト用も、同じインターフェースに従うってのがポイント」

ミナト「インターフェース……大事ですね(復唱)」

6. テストしやすいコードは良いコード!

ハルカ「DIを使ってると、テストしやすい設計が自然にできるようになる」

ミナト「確かに!やってることはシンプルなのに、すごく柔軟になる気がします」

ハルカ「あと、テストのときだけDI登録を変えるって技もあるよ。HostのConfigureServicesでテスト用サービスを渡したりね」

ミナト「え、それもできるんですか!? DI、奥が深いですね……」

ハルカ「でしょ?でも、入り口は今やったモックパターンだけで十分強い武器になるよ」

まとめ: テストが楽になると、コードも育てやすくなる!

DIで得られるテストのメリット 内容
差し替え可能 テスト用実装に切り替えられる
動作の中身が見える モックで呼び出し確認できる/td>
よりシンプルなテストが書ける DIのおかげで依存解決が楽/td>
自然と責務が分かれていく 設計も整いやすくなる/td>