メインコンテンツまでスキップ

第29章:依存ってなに?(時間・乱数・外部)⏰🎲

ここからが「テストしやすい設計」の入口だよ〜!🚪💡 この章は、テストを壊す“外部要因(依存)”を見つけられる目を作る回です👀🧪


0. この章のゴール🎯

画像を挿入予定

読み終わったら、これができるようになるよ😊✨

  • 「このコード、何に依存してる?」を言葉にできる🗣️
  • 依存を 時間⏰ / 乱数🎲 / 外部I/O🌐💾 に分類できる🧺
  • “たまに落ちるテスト(フレーク)😵‍💫”が なぜ起きるか説明できる
  • 自分の題材で 依存リストを作れる📝

1. そもそも「依存」ってなに?🤔🔌

超ざっくり言うと…

コードの外側にあって、あなたが自由にコントロールできないもの (そして、テストの結果を不安定にしがちなもの)

代表例はこの3つが鉄板👑

✅ 依存の三大カテゴリ

  • 時間:今何時?今日何日?締切超えた?⏰📅
  • 乱数:ガチャ、抽選、ランダムID、シャッフル🎲
  • 外部:ファイル、DB、ネット、OS、環境変数、Web API💾🌐🖥️

2. 依存があると、何が困るの?😵‍💫💥

テストが理想的なのはこれ👇

  • **いつ実行しても同じ結果(決定性)**🎯
  • 速い(サクサク回る)
  • **他と独立してる(順番に依存しない)**🧍‍♀️🧍‍♂️
  • 外部が落ちてもテストは落ちない🛡️

でも依存があると…こうなる😇

  • 23:59に走ったら落ちる(翌日になった)🌙➡️🌅
  • 乱数でたまに外れる🎲
  • CIだけ落ちる(ネット遅い/権限違う/時刻違う)🏗️💥
  • 他のテストと同時実行で壊れる(共有状態)🧵💣

ちなみに、xUnitは テストコレクションを並列実行するのがデフォルトなので、共有状態やグローバル依存があると事故りやすいよ〜⚠️🧵(設定で無効化もできる)(xunit.net)


3. まずは“依存レーダー”を頭に入れる📡🧠

コードを見たら、これが出てきた時点で「依存だ!」って反射してOK🙆‍♀️✨

⏰ 時間依存あるある

  • DateTime.Now / DateTimeOffset.Now
  • DateTime.UtcNow / Stopwatch / Task.Delay / Thread.Sleep

🎲 乱数依存あるある

  • Random.Shared / new Random()(シードが固定されてない)
  • Guid.NewGuid()(実質ランダム)

🌐💾 外部依存あるある

  • File.ReadAllText / File.WriteAllText
  • DBアクセス、HTTP、クラウドSDK
  • Environment.GetEnvironmentVariable
  • OSロケール、タイムゾーン、マシン名、カレントディレクトリ

4. ミニ題材:推し活アプリの「今日のピックアップ」🎀📦✨

4-1. まず“ダメな例”(依存が隠れてる)🙅‍♀️💥

「今日のおすすめグッズをランダムで1個出す」機能を、ついこう書いちゃう👇

public sealed class DailyPickup
{
public string Pick(IReadOnlyList<string> items)
{
// 隠れ依存:現在日時
var today = DateTimeOffset.Now.Date;

// 隠れ依存:乱数(しかも共有)
var index = Random.Shared.Next(items.Count);

return $"{today:yyyy-MM-dd}: {items[index]}";
}
}

これ、テストが超つらい😵‍💫

  • 実行日によって文字列が変わる⏰
  • 乱数で結果が変わる🎲
  • 並列実行で揺れる可能性もある🧵

4-2. “フレークなテスト”の例😇🎲

using Xunit;

public sealed class DailyPickupTests
{
[Fact]
public void Pick_returns_expected_item()
{
var sut = new DailyPickup();
var items = new[] { "アクスタ", "缶バッジ", "ペンライト" };

var result = sut.Pick(items);

// これ、当たる日もあれば外れる日もある…😇
Assert.Contains("アクスタ", result);
}
}

「テストがたまに落ちる」って、メンタル削るやつ…🫠🪓 だからここで 依存を見える化するよ!


5. 今日の結論:依存は“悪”じゃない。隠すのが良くない😌🔦

✅ 合言葉

  • **依存は、外に出す(見える化する)**👀✨
  • 中心ロジックは“純粋(ピュア)”に寄せる🧼
  • 外部I/Oは“端っこ(境界)”に寄せる🚪

6. 依存を見える化する:一番カンタンなやり方(引数で渡す)📦✨

この章では、まだインターフェースもDIも最小でOK🙆‍♀️ まずは 「必要なものを引数にする」 が一番わかりやすいよ!

public sealed class DailyPickup
{
public string Pick(
IReadOnlyList<string> items,
DateOnly today, // ⏰ 時間依存を外に出した
Random rng) // 🎲 乱数依存を外に出した
{
var index = rng.Next(items.Count);
return $"{today:yyyy-MM-dd}: {items[index]}";
}
}

✅ テストが安定する(決定性)🎯✨

using Xunit;

public sealed class DailyPickupTests
{
[Fact]
public void Pick_is_deterministic_with_fixed_inputs()
{
var sut = new DailyPickup();
var items = new[] { "アクスタ", "缶バッジ", "ペンライト" };

var today = new DateOnly(2026, 1, 18);
var rng = new Random(12345); // シード固定🎲🔒

var result1 = sut.Pick(items, today, rng);

// 同じシードを使うなら、同じ順序で再現できる✨
rng = new Random(12345);
var result2 = sut.Pick(items, today, rng);

Assert.Equal(result1, result2);
}
}

ポイントは「テストが世界を支配できてる」感じになること👑🧪✨ “今が何時か”も“何が出るか”も、テストが決める!


7. もう一段いい感じ:.NETの公式な“時間の抽象”TimeProvider⏰🧰

「時間って引数で渡せばいい」でも全然OKなんだけど、 最近の .NET では TimeProvider が用意されてるよ〜✨(.NET 8で導入)(Microsoft Learn)

  • TimeProvider を使うと **“時間を差し替え可能”**にできて
  • テストで「今」を固定できる🎯

さらに FakeTimeProvider(テスト用)も NuGet で用意されてる💖 Microsoft.Extensions.TimeProvider.Testing に入ってるよ(Microsoft Learn)

ここは次章以降(依存の差し替え)で本格的に使うけど、 「こういう公式の道具がある」って知っておくと安心😊✨


8. 外部依存(ファイル/ネット/DB)はどう扱う?💾🌐

✅ 初心者向けの鉄則(まずこれだけ覚えて)📌

  • ユニットテストでは 外部に触れない(触れるなら別枠)🧪🚫
  • ロジックは「データを受け取って返す」に寄せる📦➡️📦
  • ファイル読むのは“外側”、計算するのは“中心”に分ける🚪🧠

たとえば…

❌ 中心でファイル読む(テストしにくい)

public int LoadAndCount(string path)
{
var text = File.ReadAllText(path); // 💾外部依存
return text.Length;
}

✅ 外部は外で、中心はピュアに

public int CountTextLength(string text) => text.Length;

こうするとテストは超楽ちん🎉


9. 実践ワーク:依存リストを作ろう📝✨(この章の“手を動かす”)

ワークA:依存を見つける(3分)🔍

次のコードの「依存」を全部書き出して、分類してみてね👇

public sealed class ReceiptService
{
public string CreateReceipt(string customerId)
{
var now = DateTimeOffset.Now; // ?
var id = Guid.NewGuid().ToString("N"); // ?
var path = $"receipts/{customerId}.txt";

File.AppendAllText(path, $"{now:o},{id}\n"); // ?
return id;
}
}

答え(例)

  • ⏰ 時間:DateTimeOffset.Now
  • 🎲 乱数:Guid.NewGuid()
  • 💾 外部:File.AppendAllText、ファイルパス(環境差)

ワークB:自分の題材で「依存リスト」を作る(10分)🧠📝

あなたの今の題材(例:推し活グッズ管理 / カフェ会計)で、これを埋めてみて👇

依存リスト(テンプレ)

  • ⏰ 時間:例)締切判定、日付表示、期限切れ…
  • 🎲 乱数:例)抽選、ID生成、シャッフル…
  • 💾 外部I/O:例)ファイル保存、DB、HTTP、設定…
  • 🧵 共有状態:例)static、シングルトン、キャッシュ…
  • 🖥️ 環境差:例)タイムゾーン、カルチャ、パス、権限…

10. AIの使いどころ(この章は相性いい🤖💖)

✅ 依存洗い出しに使うプロンプト例

  • 「このコードの依存(時間/乱数/外部I/O)を列挙して、テストが不安定になる理由も説明して」
  • 「依存を“引数で渡す形”に直すなら、最小の変更案を出して」
  • 「この処理の“境界(I/O)”と“中心ロジック”を分ける案を3つ出して」

AIは“見落とし探し”が得意だから、依存リストの穴埋めにめちゃ便利だよ🧠✨


11. まとめ🎁✨(この章で一番大事)

  • 依存は 時間⏰ / 乱数🎲 / 外部💾🌐 がまず三本柱
  • テストが壊れる原因はだいたい 依存が隠れてるせい
  • まずは 引数で外に出すだけで、テストが安定する🎯
  • xUnitは並列実行が絡むので、共有状態やグローバル依存は特に注意🧵⚠️(xunit.net)
  • .NETには TimeProvider(時間の抽象)もあるよ⏰🧰(Microsoft Learn)

次章につながる予告📣✨(第30章)

次は今日見つけた依存を、“差し替え可能”にする最小の形を作るよ〜!🔁 「Clockをinterface化」ってやつだね⏰➡️🧩


参考:この教材で前提にしてる“周辺の最新状況”メモ🧪🛠️

  • xUnit v3 は NuGet の xunit.v3(例:3.2.2)として提供されてるよ(nuget.org)
  • .NET 10 系は 2026-01-13 に 10.0.2 のアップデートが出てる(Microsoft for Developers)
  • Visual Studio 側も .NET 10 / C# 14 を扱う前提の情報が出てる(Insiders/Preview系)(visualstudio.microsoft.com)

必要なら、この第29章の内容を「推し活グッズ管理①」に直結させて、**“依存リスト → テスト設計 → 次章の差し替え”**まで一気に通す演習にして出すよ🎀🧪✨