🐥
駆け出しエンジニアほどテスト駆動開発をしたほうが良い
はじめに
テスト駆動開発(TDD)は
「意識の高い人がやるもの」だと思われがちです。
違います。
理由は単純です。
設計力がまだ育っていないからです。
そしてTDDは、
設計を強制的に良くする仕組みだからです。
テストを書こうとすると、何が起きるか
テストを書こうとすると、必ずこうなります。
-
メソッドを分ける
-
依存を減らす
-
ロジックを純粋化する
-
staticを避ける
-
時刻やDBを直接呼ばなくなる
なぜか?
テストできないからです。
テストできないコードの例
public class OrderService
{
public void Process(Order order)
{
var db = new AppDbContext();
if (order.Amount > 10000)
{
order.Discount = order.Amount * 0.1m;
}
db.Orders.Add(order);
db.SaveChanges();
}
}
テストしようとすると:
-
DbContextが直接newされている
-
副作用だらけ
-
戻り値がない
-
ロジックと永続化が混ざっている
つまり、設計が悪い。
TDDをやるとどうなるか
まず最初にテストを書きます。
例として割引の計算ルールのテストを書きます。
[Fact]
public void 10000円以上は10%割引される()
{
var calculator = new DiscountCalculator();
var result = calculator.Calculate(10000m);
Assert.Equal(1000m, result);
}
上記のように実装しました。しかし先ほどのOrderServiceでテストができません。
なぜなら OrderService は 割引計算だけのクラスではない からです。
- 「割引ロジック」以外の要素が混ざっていて、ルールだけを検証できない
だから下記のように分けます。
ロジックを分離する
public class DiscountCalculator
{
public decimal Calculate(decimal amount)
{
if (amount > 10000)
return amount * 0.1m;
return 0;
}
}
その結果OrderServiceは下記のようになります。
public class OrderService
{
private readonly DiscountCalculator _calculator;
private readonly IOrderRepository _repository;
public OrderService(
DiscountCalculator calculator,
IOrderRepository repository)
{
_calculator = calculator;
_repository = repository;
}
public void Process(Order order)
{
order.Discount = _calculator.Calculate(order.Amount);
_repository.Save(order);
}
}
-
メソッドが分かれた
-
依存が抽象化された
-
ロジックが純粋化された
-
副作用が隔離された
自然と設計が良くなっていると思います。
TDDは、
設計改善を強制するプロセスです。
駆け出しにこそ必要な理由
駆け出しエンジニアは:
-
責務の分離が甘い
-
依存を意識できない
-
副作用を気にしない
-
とりあえず動かす
これは能力の問題ではなく経験が足りないだけです。
TDDをやると、
設計を考えざるを得ない。
だから成長が加速します。
まとめ
テスト駆動開発は、
-
バグを減らすため
-
カバレッジを上げるため
-
意識を高めるため
ではありません。
駆け出しエンジニアほど、
まずテストを書いてください。
その過程で、
-
メソッドが分かれ
-
依存が減り
-
ロジックが純粋化し
設計が洗練されていくと思います。
Discussion