第18章:ここまでの総合ミニ演習:カフェ会計①☕️🧾
この章は「いままでの型(AAA・命名・境界値・刻み方)」を、1つの小さい題材でぜんぶ使う回だよ〜!😊🎉 今日は テスト3本だけでOK👌(追加は“おかわり”で✨)
0) 今日つくる“最小仕様”📌(まずはシンプル!)
カフェ会計の合計金額(円)を出すだけ☕️
- 注文は「明細の配列(複数OK)」
- 各明細は:
単価(円) × 数量を足して 小計 - 税は 10%(小計 × 0.10)
- 税の端数は 1円未満切り捨て(floor)
- 合計 = 小計 + 税
- 注文が空なら合計は 0 円
端数ルール(切り捨て)をテストで固定するのが今日のキモだよ〜🎯✨
1) 今日のゴール🥅✨

できたら勝ち!🎉
- ✅ AAA でテストがスッと読める
- ✅ 命名が「仕様」になってる
- ✅ 境界値(端数/空注文)を押さえる
- ✅ Red → Green → Refactor を小さく3回まわせる
2) プロジェクトの形(最小)🏗️🧪
作るのはこの2つだけ:
CafeCheckout(本体:クラスライブラリ)CafeCheckout.Tests(テスト)
テストフレームワークは xUnit v3系が最新安定だよ〜🧪✨(2026/01/18時点で xunit.v3 3.2.2、VSアダプタは xunit.runner.visualstudio 3.1.5) (xUnit.net)
3) まずはテストを書く(Red)🚦🔴
テストファイル:CafeCheckoutTests.cs
狙い:**「1品だけ」**の合計(小計+税)が出ること☕️
using Xunit;
public class CafeCheckoutTests
{
[Fact]
public void Given_single_item_When_calculate_total_Then_subtotal_plus_tax_is_returned()
{
// Arrange
var items = new[]
{
new LineItem(name: "Coffee", unitPriceYen: 500, quantity: 1),
};
// Act
var total = CafeCheckout.CalculateTotalYen(items);
// Assert
Assert.Equal(550, total); // 500 + tax(50)
}
}
ここで当然コンパイル通らないよね😇(LineItemもCafeCheckoutも無い)
それでOK!それが Red 🔴✨
4) 最小実装で通す(Green)🟢✨
本体側:CafeCheckout に最小の形を作る
public readonly record struct LineItem(string Name, long UnitPriceYen, int Quantity);
public static class CafeCheckout
{
private const decimal TaxRate = 0.10m;
public static long CalculateTotalYen(IEnumerable<LineItem> items)
{
ArgumentNullException.ThrowIfNull(items);
long subtotal = 0;
foreach (var item in items)
{
subtotal += item.UnitPriceYen * item.Quantity;
}
var tax = (long)Math.Floor(subtotal * TaxRate);
return subtotal + tax;
}
}
✅ テスト実行して 緑🟢 になった?🎉 なったら次!「まだ美しくしない」よ〜!ここは最短でOK🙆♀️✨
5) 2本目:複数明細の合算(Red → Green)🧾➕🧾
次は「複数アイテムの合算」🙂
[Fact]
public void Given_multiple_items_When_calculate_total_Then_sum_and_tax_are_applied()
{
// Arrange
var items = new[]
{
new LineItem("Coffee", 500, 1), // 500
new LineItem("Cake", 400, 2), // 800
};
// Act
var total = CafeCheckout.CalculateTotalYen(items);
// Assert
// subtotal = 1300, tax = 130, total = 1430
Assert.Equal(1430, total);
}
これ、いまの実装でたぶん通るよね?😆🟢 通ったら「Redが短すぎる…」って思うけど、そういう回もある!OK!👌✨
6) 3本目:端数(切り捨て)を固定(Red → Green)🎲🚫
ここが今日の山場⛰️✨ 小計が101円だと税は 10.1円 → 10円(切り捨て) にしたい!
[Fact]
public void Given_fractional_tax_When_calculate_total_Then_tax_is_floored()
{
// Arrange
var items = new[]
{
new LineItem("Mini", 101, 1),
};
// Act
var total = CafeCheckout.CalculateTotalYen(items);
// Assert
// subtotal = 101, tax = floor(10.1) = 10, total = 111
Assert.Equal(111, total);
}
いまの実装は Math.Floor だから通るはず🟢✨
もし通らなかったら、**税の計算方法(decimal/丸め)**がズレてる可能性大だよ〜🔍💦
7) ここでリファクタ(Refactor)🧹✨(でも“小さく”ね!)
目的:読みやすくする(仕様は変えない)📘
例えば、小計計算と税計算を分けるだけで、意図がスッと入るよ🙂
public static class CafeCheckout
{
private const decimal TaxRate = 0.10m;
public static long CalculateTotalYen(IEnumerable<LineItem> items)
{
ArgumentNullException.ThrowIfNull(items);
var subtotal = CalculateSubtotal(items);
var tax = CalculateTax(subtotal);
return subtotal + tax;
}
private static long CalculateSubtotal(IEnumerable<LineItem> items)
{
long subtotal = 0;
foreach (var item in items)
{
subtotal += item.UnitPriceYen * item.Quantity;
}
return subtotal;
}
private static long CalculateTax(long subtotal)
=> (long)Math.Floor(subtotal * TaxRate);
}
✅ リファクタのたびにテスト実行🟢🟢🟢 「緑のまま整える」が最強だよ〜🛡️✨
8) “空注文”はどうする?(今日の小さい境界値)🍩➡️0円
ここは追加で1本だけ(すぐ終わるやつ)🎯
[Fact]
public void Given_no_items_When_calculate_total_Then_zero_is_returned()
{
// Arrange
var items = Array.Empty<LineItem>();
// Act
var total = CafeCheckout.CalculateTotalYen(items);
// Assert
Assert.Equal(0, total);
}
これも今の実装なら通るね🟢✨ 「空」って境界値だから、こうやってテストに残すのえらい👏😊
9) 今日の“コミットの切り方”例(めちゃ大事)💾✨
おすすめはこんな感じ👇(超小刻み!)
- 🔴
Add failing test for single item total - 🟢
Make single item test pass with minimal implementation - 🧹
Extract subtotal/tax helpers (no behavior change) - 🔴
Add test for multiple items - 🟢
Make multiple items test pass(通ってたならそのままでもOK) - 🔴
Add rounding (floor) tax test - 🟢
Ensure tax rounding uses floor(すでにOKなら確認コミットでもOK) - 🔴
Add empty order test(おかわり) - 🟢
Pass empty order test
10) AIの使いどころ(この章は“テスト案だけ”🤖🧪)
実装をAIに丸投げすると、Greenで作り込みがちで事故るから⚠️ 今日はこれがおすすめ〜😊✨
✅ 使ってOKプロンプト例
- 「この仕様のテストケースを、正常/境界値で10個ちょうだい」
- 「端数処理の境界値(101円みたいな)をもっと挙げて」
- 「いまのテスト名、誤解が少ない命名案を3つ出して」
❌ 今日は封印(事故りやすい)
- 「実装全部書いて」👉 だいたい作り込みすぎる😵💫
11) できたかチェックリスト✅✨
- ✅ テストが AAA の形でそろってる?
- ✅ テスト名だけで仕様わかる?📝
- ✅ 税の端数(切り捨て)がテストで固定されてる?🎯
- ✅ 1回の変更がデカくなってない?(10分以内目安⏱️)
12) 2026/01/18時点の“最新情報メモ”📌✨(リサーチ結果)
- 2026年1月のサービス更新で .NET 10 は 10.0.2 が案内されてるよ (Microsoft for Developers)
- C# 14 は .NET 10 でサポートされてるよ (Microsoft Learn)
- Visual Studio 2026 は 2026/01/13 に 18.2.0 の更新が出てるよ (Microsoft Learn)
- xUnit v3 は
xunit.v3 3.2.2、VSアダプタはxunit.runner.visualstudio 3.1.5が最新安定として並んでるよ (xUnit.net)
おかわり(次章へつながる追加課題)🍰✨
余裕があったら1本だけ追加してみてね😊
- 🧪 数量0はOKにする?NGにする?(仕様を決めてテストにする)
- 🧪 単価マイナスはどうする?(例外にする?無視?)
- 🧪 税率を固定じゃなく引数にする(でもやりすぎ注意!)
次は(第19章)で、**「仮実装」**っていう“通すための技”を使って、もっと気持ちよく回せるようにするよ〜🩹✨