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

2025年4月25日金曜日

AIに聞いてみた C#

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

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

登場人物

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

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

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

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

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

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

C#:

public class MyApp
{
    private Logger _logger = new Logger();

    public void Run() => _logger.Write("Hello");
}

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

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

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

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

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

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

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

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

C#:

public interface ILogger
{
    void Write(string message);
}

public class ConsoleLogger : ILogger
{
    public void Write(string message) => Console.WriteLine(message);
}

public class MyApp
{
    private readonly ILogger _logger;

    public MyApp(ILogger logger)
    {
        _logger = logger;
    }

    public void Run() => _logger.Write("Hello");
}

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

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

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

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

C#:

public class MockLogger : ILogger
{
    public string? LastMessage { get; private set; }

    public void Write(string message)
    {
        LastMessage = message;
    }
}

[TestMethod]
public void Run_LogsHello()
{
    var logger = new MockLogger();
    var app = new MyApp(logger);

    app.Run();

    Assert.AreEqual("Hello", logger.LastMessage);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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