インターフェースによるFakeとテストデータビルダー
インターフェース経由で依存関係を注入し、動作を観察する簡単なFakeを作り、小さなテストデータビルダーで複雑な入力を組み立てます。
「インターフェースによるFakeとテストデータビルダー」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン2/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全3レッスンが含まれています。
フェイクによる分離
目標: テスト対象のコードを分離します。
- インターフェースに依存します
- テストでフェイクを提供します
- 複雑な入力には小さなビルダーを使用します
- 現実の環境ではなく、呼び出しを観測します
IClock による時刻の注入
時刻の取得を IClock の背後に隠します。テストでは予測可能な結果を得るためにフェイクのクロックを渡します。
using System;
public interface IClock { DateTime NowUtc(); }
public sealed class SystemClock : IClock
{
public DateTime NowUtc() { return DateTime.UtcNow; }
}
public sealed class DiscountService
{
private readonly IClock _clock;
public DiscountService(IClock clock) { _clock = clock; }
// 50% off on Black Friday (Nov 27), else 0
public int GetDiscountPercent()
{
DateTime d = _clock.NowUtc().Date;
if (d.Month == 11 && d.Day == 27) return 50;
return 0;
}
}
public class Program
{
public static void Main(string[] args)
{
DiscountService live = new DiscountService(new SystemClock());
Console.WriteLine("Today's discount: " + live.GetDiscountPercent() + "%");
}
}
テストでのフェイク実装
フェイクはインターフェースを実装し、アサーションを安定させるために制御されたデータを返します。
using System;
public interface IClock { DateTime NowUtc(); }
public sealed class FakeClock : IClock
{
private readonly DateTime _fixed;
public FakeClock(DateTime fixedUtc) { _fixed = fixedUtc; }
public DateTime NowUtc() { return _fixed; }
}
public static class Assert
{
public static void AreEqual(int expected, int actual)
{
if (expected != actual)
throw new Exception("Expected " + expected + " but got " + actual);
}
}
public sealed class DiscountService
{
private readonly IClock _clock;
public DiscountService(IClock clock) { _clock = clock; }
public int GetDiscountPercent()
{
DateTime d = _clock.NowUtc().Date;
if (d.Month == 11 && d.Day == 27) return 50;
return 0;
}
}
public class Program
{
public static void Main(string[] args)
{
try
{
// Black Friday: 2015-11-27
DiscountService s1 = new DiscountService(new FakeClock(new DateTime(2015, 11, 27, 0, 0, 0, DateTimeKind.Utc)));
Assert.AreEqual(50, s1.GetDiscountPercent());
// Normal day
DiscountService s2 = new DiscountService(new FakeClock(new DateTime(2015, 11, 26, 0, 0, 0, DateTimeKind.Utc)));
Assert.AreEqual(0, s2.GetDiscountPercent());
Console.WriteLine("PASS: discount logic with fake clock");
}
catch (Exception ex)
{
Console.WriteLine("FAIL: " + ex.Message);
}
}
}
副作用の観測
フェイクで呼び出しを記録すれば、実際のメールや SMS を使わずに副作用を検証できます。
using System;
using System.Collections.Generic;
public interface INotifier { void Send(string message); }
public sealed class OrderService
{
private readonly INotifier _notifier;
public OrderService(INotifier notifier) { _notifier = notifier; }
public void Place(string product, int qty)
{
if (qty <= 0) throw new ArgumentOutOfRangeException("qty");
// ...
_notifier.Send("Placed: " + product + " x" + qty);
}
}
public sealed class FakeNotifier : INotifier
{
public readonly List<string> Messages = new List<string>();
public void Send(string message) { Messages.Add(message); }
}
public static class Assert
{
public static void IsTrue(bool condition, string message)
{
if (!condition) throw new Exception(message);
}
}
public class Program
{
public static void Main(string[] args)
{
FakeNotifier fake = new FakeNotifier();
OrderService svc = new OrderService(fake);
try
{
svc.Place("Book", 2);
Assert.IsTrue(fake.Messages.Count == 1, "Expected exactly one notification");
Assert.IsTrue(fake.Messages[0].IndexOf("Book") >= 0, "Message should mention product");
Console.WriteLine("PASS: observed notification via fake");
}
catch (Exception ex)
{
Console.WriteLine("FAIL: " + ex.Message);
}
}
}
テストデータビルダー
小さなビルダーでデフォルト値を設定し、テストに必要な部分だけを上書きすると、セットアップを短くわかりやすくできます。
using System;
public sealed class Order
{
public string Product;
public int Quantity;
public double UnitPrice;
public double Total() { return Quantity * UnitPrice; }
}
// Minimal builder for tests
public sealed class OrderBuilder
{
private string _product = "Item";
private int _qty = 1;
private double _price = 1.0;
public OrderBuilder WithProduct(string name) { _product = name; return this; }
public OrderBuilder WithQty(int qty) { _qty = qty; return this; }
public OrderBuilder WithPrice(double price) { _price = price; return this; }
public Order Build()
{
Order o = new Order();
o.Product = _product;
o.Quantity = _qty;
o.UnitPrice = _price;
return o;
}
}
public static class Assert
{
public static void AreEqual(double expected, double actual)
{
if (Math.Abs(expected - actual) > 0.0001)
throw new Exception("Expected " + expected + " but got " + actual);
}
}
public class Program
{
public static void Main(string[] args)
{
try
{
// Quick setup with readable intent
Order o = new OrderBuilder()
.WithProduct("Pen")
.WithQty(3)
.WithPrice(2.5)
.Build();
Assert.AreEqual(7.5, o.Total());
Console.WriteLine("PASS: builder created valid order");
}
catch (Exception ex)
{
Console.WriteLine("FAIL: " + ex.Message);
}
}
}
実践的なヒント
ヒント:
- 副作用を分離するには、小さなインターフェースを優先します
- フェイクは小さく保ち、出力をフィールドやリストに記録します
- ビルダーには妥当なデフォルト値とフルーエントな setter を用意します
- テストは物語のように読めるようにします: Arrange → Act → Assert
分離のアプローチ
まとめ
まとめ: インターフェースを注入し、フェイクで動作を制御および観測して、小さなテストデータビルダーで入力をすばやく作成します。
よくある質問
「インターフェースによるFakeとテストデータビルダー」レッスンは無料ですか?
はい。「インターフェースによるFakeとテストデータビルダー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全3レッスンが含まれています。
「インターフェースによるFakeとテストデータビルダー」で何を学びますか?
インターフェース経由で依存関係を注入し、動作を観察する簡単なFakeを作り、小さなテストデータビルダーで複雑な入力を組み立てます。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/3です。
「インターフェースによるFakeとテストデータビルダー」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- xUnit/MSTestのセットアップとアサーション
- インターフェースによるFakeとテストデータビルダー
- 失敗するテストのデバッグ