「テスト駆動開発」読書会 Vol.3の資料です。
前回のTDD読書会vol2 資料
Flyweightパターン
- 通貨の概念を導入した
- currencyをサブクラスからMoneyクラスに移動した
- Moneyのファクトリメソッドでcurrencyをインジェクションさせるように変更した
- サブクラスのコンストラクタを親クラスであるMoneyに移動させた
変更のステップは小さくコントロールできる。
大きな歩幅で進んで失敗すると後戻りが大きい。
数分で終わる小さい変更に分解し、小さい歩幅で変更する。
TDDを行う場合は常に微調整を行う。
細かなステップが窮屈なら歩幅を大きくする。
正しい歩幅というものは存在しない。
/**
* @test
*/
public function testCurrency()
{
$this->assertEquals('USD', Money::dollar(1)->currency());
$this->assertEquals('CHF', Money::franc(1)->currency());
}
Currencyの等価性を比較しているが、 dollar(1)
のように1は意味があるのか?
→ ない
今の設計上そうするしかない。
TDDの歩幅はみんなどうしているか?
「テスト駆動開発」本は歩幅が小さすぎる時もあるように感じる。
「テスト駆動開発」本は、歩幅を強調しているがゆえにステップを小さくしている。
初学者は窮屈に感じるかも。
[hamuhamu@ohashinoMacBook-Pro] % make test
phpunit --configuration phpunit.xml test/
PHPUnit 6.3.0 by Sebastian Bergmann and contributors.
..F. 4 / 4 (100%)
Time: 57 ms, Memory: 8.00MB
There was 1 failure:
1) App\MoneyTest::testFrancMultiplication
App\Money Object &0000000057c1698d000000002aad21f4 (
'amount' => 10
'currency' => 'CHF'
) is not instance of expected class "App\Franc".
--- Expected
+++ Actual
@@ @@
-App\Franc Object &0000000057c16992000000002aad21f4 (
+App\Money Object &0000000057c1698d000000002aad21f4 (
/Users/hamuhamu/Test_Driven_Development_By_Example/test/MoneyTest.php:39
FAILURES!
Tests: 4, Assertions: 10, Failures: 1.
make: *** [test] Error 1
PHPUnitの場合、toStringメソッドを実装しても出力結果が変わらなかった。
[hamuhamu@ohashinoMacBook-Pro] % make test
phpunit --configuration phpunit.xml test/
PHPUnit 6.3.0 by Sebastian Bergmann and contributors.
..F. 4 / 4 (100%)
Time: 64 ms, Memory: 8.00MB
There was 1 failure:
1) App\MoneyTest::testFrancMultiplication
App\Money Object &000000004c68270500000000273003eb (
'amount' => 10
'currency' => 'CHF'
) is not instance of expected class "App\Franc".
--- Expected
+++ Actual
@@ @@
-App\Franc Object &000000004c68271a00000000273003eb (
+App\Money Object &000000004c68270500000000273003eb (
/Users/hamuhamu/Test_Driven_Development_By_Example/test/MoneyTest.php:39
FAILURES!
Tests: 4, Assertions: 10, Failures: 1.
make: *** [test] Error 1
テストが通った。
[hamuhamu@ohashinoMacBook-Pro] % make test
phpunit --configuration phpunit.xml test/
PHPUnit 6.3.0 by Sebastian Bergmann and contributors.
.... 4 / 4 (100%)
Time: 72 ms, Memory: 8.00MB
OK (4 tests, 11 assertions)
- equalsメソッドでクラスではなくcurrencyを比較するように変更した
- FrancとDollarのtimesメソッドでMoneyを返すようにした
- FrancとDollarのtimesメソッドをMoneyに移した
toStringはJavaの言語の仕様を知っている前提のデバッグである。
レッドバーが出ているときに新しくテストを加えるのを避けたい
テストをグリーンのまま進めるのがTDDでの最短距離
特定地点へのソースコードの戻し方について
「テスト駆動開発」が発刊された当初は、Gitが存在しなかった。
今は、Gitで細かにコミットを残し戻りたい地点に戻れる。
コミットがたまり過ぎたら、rebaseなどをかける。
- Moneyのファクトリメソッドで子クラスではなくMoneyを返すように変更した
- Dollarクラスを削除した
- 不要になったテストを削除した
- Francクラスを削除した
- 不要になったtestFrancMultiplicationメソッドを削除した
/**
* @test
*/
public function testEquality()
{
$this->assertTrue(Money::dollar(5)->equals(Money::dollar(5)));
$this->assertFalse(Money::dollar(5)->equals(Money::dollar(6)));
- $this->assertTrue(Money::franc(5)->equals(Money::franc(5)));
- $this->assertFalse(Money::franc(5)->equals(Money::franc(6)));
$this->assertFalse(Money::franc(5)->equals(Money::dollar(5)));
}
3つめと4つめ消していいの?
dollarとfrancの中身を知っている前提。
TDDやってきた自信があるから、テストを消したのか。
サブクラス消すの早くないか?
あとで使うことないか?
※ 現場での一意見
このころは、IDEがなかったので一つのクラスにまとまっていたほうが都合がよかった
早足でも仮実装を行ってから、リファクタリングをする。
Imposterパターン。
良い設計を閃いたときに、テストと手入れされたコードがなければ、閃きを具体化できない。
自信を持って変更できないからだ。
hashCodeやMoneyの丸め処理が残っていたが、TODOリストはどうなった?
その場で始末してしまうことが多い
始末とは
原書ではtake care of~ と記載されている。
https://eow.alc.co.jp/search?q=take+care+of
~を処理する、~に対処する
~を殺す[片付ける・始末する]
そもそも、必要に迫られるまでTODOリストに上げないよ派もいる。
とりあえずTODOリストに上げてみて捨てる派もいる。
リストを作るとやりきらないといけない脅迫が生まれる。
低い投資対効果のTODOは捨てていく。
- このまま、5分読書、10分ディスカッションスタイルで
まとめありがとうございます。
ここは、「テストをレッドからグリーンにするための最短距離を通る」ということなので(たとえば仮実装)、
テストがレッドの状態で新しいテストを追加するのは最短距離を通ってないよね、という意味でした。