Upgrade to Pro — share decks privately, control downloads, hide ads and more …

書籍『テスト駆動開発』の紹介(みんなのPython勉強会#37 の発表資料)

書籍『テスト駆動開発』の紹介(みんなのPython勉強会#37 の発表資料)

みんなのPython勉強会#37 の資料

https://startpython.connpass.com/event/81625/presentation/

eiji.ienaga

July 11, 2018
Tweet

More Decks by eiji.ienaga

Other Decks in Programming

Transcript

  1. ⾃⼰紹介 • 家永英治 • Tiwtter: @haru01 • 永和システムマネジメント • アジャイルコーチ

    • Scrumに限らず ユニットテスト リファクタリング TDD …の良さもお伝えしたい︕ • ときどき仕事や趣味でプログラマ • 実務はJavaが⼤半、Rubyが少々。 Pythonは趣味、最近はGoを勉強中 • ブログ︓https://twop.agile.esm.co.jp/ 2
  2. 動作するきれいなコードを使って プログラマが実現したいこと ✔ プログラミング活動を制御可能にしたい • 分析⿇痺で設計はきれいだが動かないで四苦⼋苦はヤダ • テスト⼯程という名のデバック地獄で、遅れて⽋陥の対応に追われる⽇々はヤダ • はじめから予期しない事が起こる複雑さがあるが、不安で押しつぶされるのはヤダ

    ✔ コードを通じてチームメイトと信頼関係を築きたい • 動作しないコードを渡されてるのはヤダ・渡すのはヤダ • 不吉な臭いがするコードを渡されるのはヤダ・渡すのはヤダ ✔ 作ったものでユーザに快適さを届けたい • いつまでたっても壊れた動かないソフトウェアで、顧客やユーザをイライラさせるのはヤダ ✔ 気持ちよくコードを書き続けたい • 半年後、⼀年後に不吉な匂いがするコードに囲まれて、⽬の輝きを失ってしまうはヤダ 7
  3. 3.プレシャー UP 1.⼿と⽬のテスト が⼿間で確認頻度 DOWN 2.意図せず ⽋陥混⼊UP あとでまとめて⾒つけた ⽋陥の対応に 追われて。。。

    時間に追われて テストする 時間がなく。。。 11 プログラマは⽋陥対応に追われ コントロールを失い⾃信を喪失 ⼿と⽬に頼るため ⼼理的に負担のある作業 ※ 付録A、25章を参考にいろいろ追記
  4. プレッシャー UP ⼿と⽬によるテストで 実施頻度DOWN ⽋陥数 UP 技術的負債 UP 怖くて リファクタリング出来ず

    頻度DOWN 障害対応や要望 対応にてまどり。。 15 ※テスト熱中症、技術的負債を参考に記載
  5. 3.プレッシャー UP 1.⼿と⽬によるテストで 実施頻度DOWN 2.1.⽋陥数 UP 2.2.2.技術的負債 UP 2.2.1.怖くて リファクタリング出来ず

    頻度DOWN 障害対応や要望 対応にてまどり。。 意図せず ⽋陥を埋め込んで。。。 22
  6. 4.プレッシャー Down 2.機械によるテストで で実施頻度UP 2.1.⽋陥数 Down 3.2.2.技術的負債 DOWN 3-2-1.安⼼して リファクタリング

    頻度UP 障害対応や要望 対応がスムーズになり 23 1.こまめにテスト (仕様の具体例) をコード化UP 早期に発⾒して対応できるので。。
  7. TDDの定番の練習お題 • 書籍I部︓多国通貨 • 書籍II部︓テスティングフレームワーク • FizzBuzz • ボーリングゲーム •

    ⾃動販売機 • 整数の閉区間 • セマンティック・バージョニング • ライフゲーム • Etc… http://devtesting.jp/tddbc/ http://d.hatena.ne.jp/absj31/20120721/1342880403 http://cyber-dojo.org/ http://codingdojo.org/KataCatalogue/ (探せば、練習お題がいくつかある。) 38
  8. お⼿本を⾒るには︖ • 和⽥さんのFizzBuzzお題でのTDDライブ • https://channel9.msdn.com/Events/de-code/2017/DO03 • Robert-c-martinによる TDD ボーリングゲームのKATAの ステップバイステップでつくる様⼦

    • https://www.slideshare.net/lalitkale/bowling-game-kata-by- robert-c-martin (Youtubeを探せばTDDをやっている様⼦の動画がいくつか⾒つかる) 39
  9. テスト駆動開発のパターン テスト(名詞) 独⽴したテスト TODOリスト テストファースト アサートファースト テストデータ 明⽰的なデータ レッドバーのパターン ⼀歩を⽰すテスト

    はじめのテスト 説明的なテスト 学習⽤テスト 脱線はTODOリストへ 回帰テスト 休憩 やり直す 安い机に良い椅⼦ テスティングのパターン ⼩さいテスト Mock Object(擬装オブジェクト) Self Shunt(⾃⼰接続) Log String(記録⽤⽂字列) Crash Test Dummy(衝突実験ダミー⼈形) 失敗させたままのテスト きれいなチェックイン グリーンバーのパターン 仮実装を経て本実装へ 三⾓測量 明⽩な実装 ⼀から多へ xUnitのパターン アサーション フィクスチャー 外部フィクスチャー テストメソッド 例外のテスト まとめてテスト デザインパターン Command Value Object Null Object Template Method Pluggable Object Pluggable Selector Factory Method Imposter Composite Collecting Parameter Singleton リファクタリング 差異をなくす 変更の分離 データ構造の変更 メソッドの抽出 メソッドのインライン化 インタフェースの抽出 メソッドの移動 メソッドオブジェクト パラメータの追加 メソッドからコンストラクタへ のパラメータの移動 42 3部の パターン一覧
  10. テスト駆動開発のパターン テスト(名詞) 独⽴したテスト TODOリスト テストファースト アサートファースト テストデータ 明⽰的なデータ レッドバーのパターン ⼀歩を⽰すテスト

    はじめのテスト 説明的なテスト 学習⽤テスト 脱線はTODOリストへ 回帰テスト 休憩 やり直す 安い机に良い椅⼦ テスティングのパターン ⼩さいテスト Mock Object(擬装オブジェクト) Self Shunt(⾃⼰接続) Log String(記録⽤⽂字列) Crash Test Dummy(衝突実験ダミー⼈形) 失敗させたままのテスト きれいなチェックイン グリーンバーのパターン 仮実装を経て本実装へ 三⾓測量 明⽩な実装 ⼀から多へ xUnitのパターン アサーション フィクスチャー 外部フィクスチャー テストメソッド 例外のテスト まとめてテスト デザインパターン Command Value Object Null Object Template Method Pluggable Object Pluggable Selector Factory Method Imposter Composite Collecting Parameter Singleton リファクタリング 差異をなくす 変更の分離 データ構造の変更 メソッドの抽出 メソッドのインライン化 インタフェースの抽出 メソッドの移動 メソッドオブジェクト パラメータの追加 メソッドからコンストラクタへ のパラメータの移動 66 3部の パターン一覧
  11. [pylint] E0401:Unable to import 'dollar' 81 import pytest from dollar

    import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  12. TODO • $5 + 10CHF = $10 (レートが2:1の場合) • $5

    * 2 =$10 • amount を privateにする • Dollarの副作⽤ • Moneyの丸め処理どうする? 85
  13. 87 [pylint] E0401:Unable to import 'dollar' import pytest from dollar

    import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  14. E AttributeError: 'Dollar' object has no attribute 'amount' 88 class

    Dollar: def __init__(self, amount): pass def times(self, multiplier): pass import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  15. class Dollar: def __init__(self, amount): self.amount = 0 def times(self,

    multiplier): pass … E assert 10 == 0 E + where 0 = <dollar.Dollar object at 0x104177278>.amount 89 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  16. class Dollar: def __init__(self, amount): self.amount = 0 def times(self,

    multiplier): pass 94 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount … E assert 10 == 0 E + where 0 = <dollar.Dollar object at 0x104177278>.amount
  17. class Dollar: def __init__(self, amount): self.amount = 10 def times(self,

    multiplier): pass 95 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  18. class Dollar: def __init__(self, amount): self.amount = 10 def times(self,

    multiplier): pass 101 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  19. class Dollar: def __init__(self, amount): self.amount = 5 * 2

    def times(self, multiplier): pass 102 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  20. class Dollar: def __init__(self, amount): self.amount = amount * 2

    def times(self, multiplier): pass 103 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  21. class Dollar: def __init__(self, amount): self.amount = amount def times(self,

    multiplier): self.amount = self.amount * 2 104 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  22. class Dollar: def __init__(self, amount): self.amount = amount def times(self,

    multiplier): self.amount = self.amount * multiplier 105 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  23. class Dollar: def __init__(self, amount): self.amount = amount def times(self,

    multiplier): self.amount *= multiplier 106 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount
  24. TODO • $5 + 10CHF = $10 (レートが2:1の場合) • $5

    * 2 =$10 • amount を privateにする • Dollarの副作⽤ • Moneyの丸め処理どうする? 109
  25. TODO • $5 + 10CHF = $10 (レートが2:1の場合) • $5

    * 2 =$10 • amount を privateにする • Dollarの副作⽤ • Moneyの丸め処理どうする? 111
  26. これは無理が ある 副作⽤あり five は $10 に import pytest from

    dollar import Dollar def test_money_multiplication(): five = Dollar(5) five.times(2) assert 10 == five.amount five.times(3) assert 15 == five.amount 113
  27. > assert 10 == product.amount E AttributeError: 'NoneType' object has

    no attribute 'amount' 117 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) product = five.times(2) assert 10 == product.amount product = five.times(3) assert 15 == product.amount class Dollar: def __init__(self, amount): self.amount = amount def times(self, multiplier): self.amount *= multiplier
  28. > assert 10 == product.amount E AttributeError: 'NoneType' object has

    no attribute 'amount' 121 import pytest from dollar import Dollar def test_money_multiplication(): five = Dollar(5) product = five.times(2) assert 10 == product.amount product = five.times(3) assert 15 == product.amount class Dollar: def __init__(self, amount): self.amount = amount def times(self, multiplier): self.amount *= multiplier
  29. 122 import pytest from dollar import Dollar def test_money_multiplication(): five

    = Dollar(5) product = five.times(2) assert 10 == product.amount product = five.times(3) assert 15 == product.amount class Dollar: def __init__(self, amount): self.amount = amount def times(self, multiplier): return Dollar(self.amount * multiplier)
  30. TODO • $5 + 10CHF = $10 (レートが2:1の場合) • $5

    * 2 =$10 • amount を privateにする • Dollarの副作⽤ • Moneyの丸め処理どうする? 129
  31. 動作するきれいなコードを使って プログラマが実現したいこと ✔ プログラミング活動を制御可能にしたい • 分析⿇痺で設計はきれいだが動かないで四苦⼋苦はヤダ • テスト⼯程という名のデバック地獄で、遅れて⽋陥の対応に追われる⽇々はヤダ • はじめから予期しない事が起こる複雑さがあるが、不安で押しつぶされるのはヤダ

    ✔ コードを通じてチームメイトと信頼関係を築きたい • 動作しないコードを渡されてるのはヤダ・渡すのはヤダ • 不吉な匂いがするコードを渡されるのはヤダ・渡すのはヤダ ✔ 作ったものでユーザに快適さを届けたい • いつまでたっても壊れた動かないソフトウェアで、顧客やユーザをイライラさせるのはヤダ ✔ 気持ちよくコードを書き続けたい • 半年後、⼀年後に不吉な匂いがするコードに囲まれて、⽬の輝きを失ってしまうはヤダ 146
  32. 3.プレッシャー UP 1.⼿と⽬によるテストで 実施頻度DOWN 2.1.⽋陥数 UP 2.2.2.技術的負債 UP 2.2.1.怖くて リファクタリング出来ず

    頻度DOWN 障害対応や要望 対応にてまどり。。 意図せず ⽋陥を埋め込んで。。。 147
  33. 4.プレッシャー Down 2.機械によるテストで で実施頻度UP 2.1.⽋陥数 Down 3.2.2.技術的負債 DOWN 3-2-1.安⼼して リファクタリング

    頻度UP 障害対応や要望 対応がスムーズになり 148 1.こまめにテスト (仕様の具体例) をコード化UP 早期に発⾒して対応できるので。。
  34. テスト駆動開発のパターン テスト(名詞) 独⽴したテスト TODOリスト テストファースト アサートファースト テストデータ 明⽰的なデータ レッドバーのパターン ⼀歩を⽰すテスト

    はじめのテスト 説明的なテスト 学習⽤テスト 脱線はTODOリストへ 回帰テスト 休憩 やり直す 安い机に良い椅⼦ テスティングのパターン ⼩さいテスト Mock Object(擬装オブジェクト) Self Shunt(⾃⼰接続) Log String(記録⽤⽂字列) Crash Test Dummy(衝突実験ダミー⼈形) 失敗させたままのテスト きれいなチェックイン グリーンバーのパターン 仮実装を経て本実装へ 三⾓測量 明⽩な実装 ⼀から多へ xUnitのパターン アサーション フィクスチャー 外部フィクスチャー テストメソッド 例外のテスト まとめてテスト デザインパターン Command Value Object Null Object Template Method Pluggable Object Pluggable Selector Factory Method Imposter Composite Collecting Parameter Singleton リファクタリング 差異をなくす 変更の分離 データ構造の変更 メソッドの抽出 メソッドのインライン化 インタフェースの抽出 メソッドの移動 メソッドオブジェクト パラメータの追加 メソッドからコンストラクタへ のパラメータの移動 155 3部の パターン一覧