第6章:テストは“設計の味方”だよ🧪✨(AAA入門+安心リファクタの土台づくり)
この章はひとことで言うと… **「SOLIDをやる前に、コードを触っても怖くない状態を作ろう」**です😊💕
テストがあると、リファクタリングしても 「動きが変わってないよね?✅」を機械が毎回チェックしてくれるので、設計改善が一気にラクになります✨
6.1 この章のゴール🎯💖
この章が終わったら、次ができるようになります👇✨
- 単体テストの基本形 AAA(Arrange/Act/Assert) が書ける🧁

- “仕様を守るテスト” を数本作って、安心してコード整理できる🧹✨
- 「テストしづらいコード=設計の臭い👃💦」を見つけられる
- (ちょい最新✨)Microsoft.Testing.Platform が何者かを薄く理解する🤖 ※Visual Studioのテストエクスプローラーとも統合されてるよ~! (Microsoft Learn)
6.2 テストがあると、SOLIDが進む理由💡🧸

SOLIDって、だいたい「クラス分ける」「依存を差し替える」「境界を作る」みたいに構造を動かすんだけど…
構造を動かすのが怖い最大の理由って👇これです😇
- 動作が変わったら困る
- でも人間の目では「変わってない」を保証しきれない
だからテストがあると、
- Before/Afterの挙動が同じを短時間で何回も確認できる✅✨
- 「変更しても壊れてない」が見えるので、設計改善が進む🚀
6.3 まずは超入門:AAA(Arrange / Act / Assert)🍰✨
単体テストは、基本この3つだけ覚えればOKです😊💕
- Arrange(準備):入力データや対象クラスを用意する🧺
- Act(実行):メソッドを呼ぶ🎬
- Assert(確認):結果が期待どおりかチェックする✅
この形があるだけで、テストが読みやすくなるよ~📚✨
6.4 テストプロジェクトを作る🧰🪄
Visual Studioで作る(いちばん楽💕)
- ソリューションを右クリック → 追加 → 新しいプロジェクト
- xUnit テストプロジェクト(または MSTest / NUnit)を選ぶ
MiniECommerce.Testsみたいな名前にする🧁- テストエクスプローラー(Test Explorer)で Run All ✅
ちなみに .NET 10 は C# 14 とセットで、Visual Studio 2026 が対応しています📦✨ (Microsoft)
コマンドでも作れる(VS Code派にも◎)
dotnet new xunit -n MiniECommerce.Tests
dotnet test
6.5 サンプル題材:注文合計(割引+送料)をテストする🛒💰✨
ここから「ミニEC」の“合計金額計算”を例にします😊 テストは 「仕様の見張り番👮♀️」 って感じで作るよ!
まずはプロダクトコード(合計計算)📦
using System;
using System.Collections.Generic;
using System.Linq;
public sealed record OrderItem(string Sku, decimal UnitPrice, int Quantity);
public enum ShippingMethod
{
Standard,
Express
}
public sealed class Order
{
public List<OrderItem> Items { get; } = new();
public decimal? CouponRate { get; init; } // 0.10m = 10% OFF
public ShippingMethod Shipping { get; init; } = ShippingMethod.Standard;
}
public sealed class PriceCalculator
{
public decimal CalculateTotal(Order order)
{
if (order is null) throw new ArgumentNullException(nameof(order));
if (order.Items.Count == 0) return 0m;
var subtotal = order.Items.Sum(i => i.UnitPrice * i.Quantity);
var discount = order.CouponRate is { } rate ? subtotal * rate : 0m;
var afterDiscount = subtotal - discount;
var shippingFee = order.Shipping switch
{
ShippingMethod.Standard => afterDiscount >= 5000m ? 0m : 500m,
ShippingMethod.Express => 900m,
_ => throw new InvalidOperationException("Unknown shipping method.")
};
return afterDiscount + shippingFee;
}
}
6.6 1本目のテスト:AAAで書く🧪🍓
using Xunit;
public class PriceCalculatorTests
{
[Fact]
public void CalculateTotal_Standard_IsFreeShipping_WhenAfterDiscountIsAtLeast5000()
{
// Arrange 🧺
var order = new Order
{
Shipping = ShippingMethod.Standard,
CouponRate = 0.10m
};
order.Items.Add(new OrderItem("BOOK", 6000m, 1)); // 6000 → 10%OFF → 5400
var sut = new PriceCalculator();
// Act 🎬
var total = sut.CalculateTotal(order);
// Assert ✅
Assert.Equal(5400m, total); // 送料0円
}
}
この時点で、すでに価値が出てます✨ あとで内部の実装をどれだけ整理しても、このテストが通る限り「大丈夫そう😊」って言える!
6.7 テストを“仕様の箇条書き”にする(Theoryで増やす)🧁📌
「同じ形のテストを、入力だけ変えて何個もやりたい」ってときは Theory が便利✨
using Xunit;
public class PriceCalculatorTheoryTests
{
[Theory]
[InlineData(4000, null, ShippingMethod.Standard, 4500)] // 4000 + 送料500
[InlineData(5000, null, ShippingMethod.Standard, 5000)] // 5000 + 送料0
[InlineData(3000, 0.10, ShippingMethod.Standard, 3200)] // 3000→2700 + 送料500 = 3200
[InlineData(3000, 0.10, ShippingMethod.Express, 3600)] // 3000→2700 + 送料900 = 3600
public void CalculateTotal_WorksForCommonCases(
decimal subtotal,
double? couponRate,
ShippingMethod shipping,
decimal expected)
{
// Arrange 🧺
var order = new Order
{
Shipping = shipping,
CouponRate = couponRate is null ? null : (decimal)couponRate.Value
};
order.Items.Add(new OrderItem("SKU", subtotal, 1));
var sut = new PriceCalculator();
// Act 🎬
var total = sut.CalculateTotal(order);
// Assert ✅
Assert.Equal(expected, total);
}
}
✅ こういうテストが増えるほど、安心してSOLID改善に突っ込めるようになります🔥✨
6.8 「テストしにくいコード」は設計の赤信号🚨😵💫
よくある“テストしづらい元凶”👇
DateTime.Now/Guid.NewGuid()/Random(毎回変わる)⏰🎲new HttpClient()とか、DBアクセス直書き(外部I/O)🌐🗄️staticだらけ(差し替えできない)🧱- 1メソッドが長くて、入力と出力がぐちゃぐちゃ(I/Oとロジック混在)🍝
これ、SOLIDの入口でめっちゃ効いてきます😇 だからこの章では 「差し替えできる形にする」 を小さく体験しよ✨
6.9 例:時間に依存する処理をテスト可能にする⌛🧸
ダメな例(テストが不安定💦)
public sealed class CampaignService
{
public bool IsCampaignActive()
=> DateTime.Now.Hour >= 9 && DateTime.Now.Hour < 18;
}
テストが「朝は落ちる」「夜は通る」みたいになる😵💫💥
いい例:時計を外から渡す🎁✨
public interface IClock
{
DateTime Now { get; }
}
public sealed class SystemClock : IClock
{
public DateTime Now => DateTime.Now;
}
public sealed class CampaignService
{
private readonly IClock _clock;
public CampaignService(IClock clock) => _clock = clock;
public bool IsCampaignActive()
=> _clock.Now.Hour >= 9 && _clock.Now.Hour < 18;
}
テスト(好きな時間を作れる💖)
using Xunit;
public sealed class FakeClock : IClock
{
public DateTime Now { get; set; }
}
public class CampaignServiceTests
{
[Fact]
public void IsCampaignActive_ReturnsTrue_DuringBusinessHours()
{
// Arrange 🧺
var clock = new FakeClock { Now = new DateTime(2026, 1, 9, 10, 0, 0) };
var sut = new CampaignService(clock);
// Act 🎬
var active = sut.IsCampaignActive();
// Assert ✅
Assert.True(active);
}
}
これが 「依存を外から渡す」 の超ミニ版🎀 あとでDIP/DIに入ったとき、「あ、これだ!」ってなるやつです😊✨
6.10 テスト実行の“最新寄り”メモ🧪🤖
最近の .NET では Microsoft.Testing.Platform という軽量テスト実行基盤が整備されていて、CLI/CI/Visual Studio/VS Code など色んな場面で動かしやすい方向です✨ (Microsoft Learn) xUnit v3 も Microsoft Testing Platform にネイティブ対応しています📦 (xunit.net)
(でもね!)初心者のうちは難しく考えなくてOK😊 Test Explorerで回せて、dotnet test で回せたら勝ち🏆 です✨
6.11 おまけ:カバレッジを“ざっくり”取る🧁📈
「テストがどこまで通ってるか」をざっくり見るなら、dotnet test --collect:"XPlat Code Coverage" が定番✨
テンプレートも既定で coverlet と統合されてる説明があります📌 (Microsoft Learn)
dotnet test --collect:"XPlat Code Coverage"
カバレッジは 高ければ偉いじゃなくて、 「大事な分岐・大事な仕様を守れてる?」を見るための道具だよ😊💕
6.12 AIの使いどころ(この章は相性良すぎ🤖💕)
AIにはこう頼むと強いです👇✨
- 「このメソッドのテスト観点を5〜10個、理由つきで出して」🧠
- 「境界値(0、1、最大、null、空)を列挙して」📌
- 「AAAの形でxUnitテストを書いて。命名も提案して」🧁
- 「このテスト、足りないケースある?」🔍
⚠️ 注意:AIは“それっぽいけどズレた期待値”を置くことあるので、 期待値は人間が仕様から決めるのが安全だよ😊✅
6.13 章末ミニ演習🎮✨(所要20〜40分)
演習A:送料ルールを守るテストを増やす📦
次をテストで守ってね👇😊
- Standard:5000以上なら送料0円
- Standard:4999以下なら送料500円
- Express:常に送料900円
演習B:例外・空の注文も守る😈
order = null→ArgumentNullExceptionItemsが空→ 合計0円
6.14 この章のチェックリスト✅💖
- テストはAAAで書けた🧁
- “仕様を守るテスト”が最低3〜6本ある🛡️
- テストしづらい原因(Now/Random/I/O/static)が分かる👃
- 1つだけでも依存を外から渡してテストできた🎁
次の章(第7章)では、ここで作った「安心の土台」を使って、SOLID全体を地図みたいに整理していきます🗺️🌈 「どこがどの原則に引っかかってるの?」を見分けられるようにしよ~😊✨