本文へスキップ
BecomeCoder

C#デザインパターンコース · 第2章 TDD ― テスト駆動開発 · レッスン17

Moq ― モックで依存を差し替える

ブラウザで完結

導入

DB・外部API・時刻など「本物を使うとテストが遅い・不安定」な依存先を、偽物(モック)に差し替えるNuGetライブラリがMoqです。DIP(抽象への依存)が前提になります。

図解

flowchart LR
    T["テスト対象"] --> I["インターフェース(抽象)"]
    I -->|本番| R["本物(DB/API)"]
    I -->|テスト| M["モック(Moqが生成)<br/>「こう返す」と指定"]
    style M fill:#e8f5e9

サンプル

Moqの記法はこうです(ローカルでNuGetを入れて使います)。

// --- Moq の記法(読み物) ---
// var mock = new Mock<IDiscountService>();
// mock.Setup(s => s.GetRate("VIP")).Returns(0.1m);   // 振る舞いを指定
// var calc = new PriceCalculator(mock.Object);
// Assert.Equal(900m, calc.Calculate("VIP", 1000m));
// mock.Verify(s => s.GetRate("VIP"), Times.Once());   // 呼ばれたか検証

ブラウザでは「手動フェイク」で同じ考え方を再現します(Moqはこれを自動生成する道具です)。実務と同じく、抽象・本番用の実装・テスト用の偽物をそれぞれ別ファイルに置きます。

1. 抽象(IDiscount.cs)。差し替えの継ぎ目になるインターフェースです。

public interface IDiscount
{
    double Rate();
}

2. テスト用の偽物(FakeDiscount.cs)。固定値を返すだけ。本来はテストプロジェクト側に置くファイルです。

// 手動モック(固定値を返す偽物)。Moqはこのクラスを自動生成してくれる
public class FakeDiscount : IDiscount
{
    private readonly double _rate;
    public FakeDiscount(double rate) => _rate = rate;
    public double Rate() => _rate;
}

3. テスト対象(PriceCalculator.cs)。相手が本物か偽物かを知りません。だから差し替えられます。

public class PriceCalculator
{
    private readonly IDiscount _discount;
    public PriceCalculator(IDiscount discount) => _discount = discount;
    public int Calculate(int price) => (int)(price * (1 - _discount.Rate()));
}

4. テスト(Program.cs)。偽物を注入すれば、DBもAPIも無しで一瞬で検証できます。

var calc = new PriceCalculator(new FakeDiscount(0.1));
Console.WriteLine(calc.Calculate(1000));   // 900
  • Moqはnew Mock<IXxx>()で偽物を作り、Setup(...).Returns(...)で振る舞いを指定する
  • Verify(...)で「その呼び出しが行われたか」も検証できる
  • 本質は「インターフェースの手動実装(フェイク)を自動生成してくれる道具」
  • 偽物のファイルはテスト側に置く。本番の成果物には含めない

演習

var greeter = new Greeter(new FakeClock(2026));
Console.WriteLine(greeter.Greet());   // あけましておめでとう

// TODO: IClock(Year()→int)を実装する FakeClock を定義してください(固定の年を返す)
interface IClock { int Year(); }
class Greeter
{
    private readonly IClock _clock;
    public Greeter(IClock clock) => _clock = clock;
    public string Greet() => _clock.Year() == 2026 ? "あけましておめでとう" : "こんにちは";
}
___
  • 期待される出力: あけましておめでとう
ヒント1を見る

class FakeClock : IClock { private readonly int _y; public FakeClock(int y) => _y = y; public int Year() => _y; }

ヒント2を見る

固定値を返す偽物を注入すると、時刻に依存せずテストできます

まとめ

  • Moqは依存先を偽物(モック)に差し替えるライブラリ
  • Setupで振る舞い指定、Verifyで呼び出し検証
  • 仕組みは素のC#の手動フェイクと同じ(DIが前提)

次回: 読みやすい検証を書くFluentAssertionsです。

実際に動かしてみよう

このレッスンのサンプルは、実務と同じように役割ごとの .cs ファイルへ分けてあります。下のエディタは最初からその複数ファイルが入った状態で、上のタブでファイルを切り替えられます。そのまま「▶ 実行」を押せば全ファイルをまとめてコンパイルして動かせるので、クラスを足したりファイルをまたいで書き換えたりしながら進めましょう(演習は本文のコードをコピーして Program.cs のタブに貼り付ければ試せます)。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。

次のレッスンへ →