-
元のコードにテストコードが0
- 200k行なのにテストないとか・・・
-
自社製Webフレームワーク使ってる
-
Godオブジェクトが居る
-
【レガシーコード対策】リフレクションを使って仕様化する
- いずれ捨てる
-
イベントループ的なの
- ケースを切り分けて考える
- 例えば300回回したときにどうなるか、とかは別に考える必要がある。
-
性能試験
- Gatling(http://gatling-tool.org/)
- 結合試験でも使っている。パラメータを変えれば性能試験の出来上がり。
- JMeter
- xUnit
- Gatling(http://gatling-tool.org/)
-
異常系
- GatlingやJMeterで実際的に落とす
- リフレクションなどを使って落とすケースを作り出す
- ハードウェア的に落とす
- テスト内で設定ファイルを書き換えるようにしておいて、ネットワークエラーを起こすなど
-
耐久テスト
- xUnit
- Scala
-
期待結果が人に依存する試験
- 担当者とプロダクトオーナーの認識の齟齬
- 期待結果 / 仕様が曖昧
- 定量化できればいけるケースもある
- その時点で曖昧さは無い
TDDしづらい領域を先に考えておく事で、TDDしやすい方向へ持っていく事が出来る。
-
クラスベースのOOP言語で書かれた書籍が多い。他のパラダイムに置けるTDDとは?
- C
- Javascript
- Haskell
- Scala
- F#
-
Javascript
- 最近はテストフレームワークが出揃ってきていて、何とかなる
-
Haskell
- コンパイルが通れば大体動くから、あんまりテストコード書かない
- 他の関数型言語でも大体同じ。Haskell, OCamlは型で表現できる内容が豊かなので、コンパイル通れば大体OK。
- F#はちょっと違う。
- 何のためにテストしているのか? -> 値を確認するため。
- 型は値よりも抽象的な概念で、Haskell, OCamlは型を用いて記述する。型に齟齬がなければ、大体において想定通りになる。
- コンパイラが保証している範囲であればテストを書く必要は無い。
- ただし、テストが必要ないという事ではない。
- 他の関数型言語でも大体同じ。Haskell, OCamlは型で表現できる内容が豊かなので、コンパイル通れば大体OK。
- コンパイルが通れば大体動くから、あんまりテストコード書かない
-
TDDがやりやすいかどうか ≒ 言語にどれだけ習熟しているかどうか
-
TDDはあくまでプログラマが考えるためのフレームワーク
-
関数型言語は設計がやりやすい。
- TDDでサポートできている外部設計については、型によってサポートされる。
- 関数型言語におけるTDDは、設計ではなく試験に比重がよる。
-
静的型付言語においては、テストを一行書く前に、まず型から書き始める。
- Javaにおける型とはインターフェイス
- Haskell, OCaml, F#などはより複雑な型を宣言できる。面倒くさい記述も少ない。"型が軽い"。
-
言語が前提としているパラダイムを変更してTDDする意味があるのか?
- TDD for Embedded Cは、いわゆるCスタイルでTDDするのではなく、オブジェクト指向的な設計の仕方をしましょうと書いてある。
- JavaにおけるTDDの書籍では、OOPベースの書籍しかない。他のパラダイムで説明しているものは無い。
- わざわざ別のパラダイムで考える必要は無いのでは?
- TDD for Embedded CはC言語でオブジェクト指向設計を学ぼう、という趣旨に思える。それにTDDくっつけただけのよーな。
-
TDDとWモデルの関係
- TDD, BDDはテストコードで仕様を表現する
- ではテストコードだけが仕様?テストコードだけでソフトウェアを読み解けるか?
- コード以外のレイヤの仕様や要求が存在している。
- Wモデルにおいて、前段階の定義が次の段階のテストの入力になっているのは、それ。
- 前段階の仕様からテストを書いて、同時に同じレイヤの設計をする事でテストファーストに出来る。
-
ATDD
- ある機能について実装している間はずっとRedになる。
- 開発者のマシンでAcceptance Testをやるのは非効率的?
- CI環境だけでATをやって開発者のマシンではやらない、という方法も。
- ATはなるべく早くGreenにする(そのためであればモックを大量に使う)
- GOOS的なやりかた
-
モックの功罪
- 嫌い
- 面倒くさい
- バグを作り込みやすい
- TDDの自殺(http://d.hatena.ne.jp/kyon_mm/20121223/1358326642)
- テスト通ってるのにモックが間違っている、とか
- 複雑すぎるモックを書かないこと。ドメインごとに許容される複雑さ(モックの行数とか)を定義する。
- 仕様変更に対応させるのにコストがかかる
- モックをメンテさせようとするから困難になる
- 好き
- 作るのが難しいオブジェクト
- このテストは結合テストでやっちゃうのも一つの手
- 腐敗防止層
- 使い捨てのモック
- 作るのが難しいオブジェクト
- 嫌い
-
良いTDD
- 仕様変更がしやすい状態を提供し続けている
- 良いコードを書けるようにする == ダメなコードを許さない
- TDDはObject Oriented Excerciseのようなもの
- 想定されたパラダイム以外のコードを書かない
- 想定されていないコードを書かない
- UnitTestにおいては、良いシグネチャが良いコードを保証する
- ATDDにおけるTDDが良いコードを育てているとは?何を見てそういえるか?
- ATDDで悪いコードを防げているか?
- 防げているのであれば良いATDDと言えるし、良いコードになるフォースが無いのであれば良いATDDとは言えない
- TDDは品質保証をしている
- ここで言っている品質とは、「保守性をあげる」ということ
-
悪いTDD
- 思ったタイミングで色が変わらない
- ちゃんと狙ったタイミングでRedにならなかったりすると、積み上げてきたテストへの信頼がなくなってしまう
- どこまで戻ればこのテストは信頼できるのだろうか?
- テストレベルと合っていないassertの数
- UnitTestなのに複数のassertが存在している
- 思ったタイミングで色が変わらない
-
利用と現実の狭間
- オブジェクト指向でやっていくと、オーバーヘッドが発生する
- パフォーマンスに非常にシビアな場面だと、それすらも惜しい
- その場合には汚いコードを書かざるを得ない...
- そういった場面では、まさにデザインパターンの登場
- ストラテジパターンなんかはアルゴリズムを切り替えるためにある。
- 切り出しておいて必要になればパフォーマンスの高い実装に切り替えれば良いだけ
- ストラテジパターンなんかはアルゴリズムを切り替えるためにある。
- 最初は綺麗なコードを書けば良い
- パフォーマンスを意識するのは最後の最後
- Effective C++では、最後の最後まで局所最適化はするな、と言われている
- 最適化は全体を計測してから
- パフォーマンスを意識するのは最後の最後
- オブジェクト指向でやっていくと、オーバーヘッドが発生する
- コードカバレッジなんて必要ない
- 考えるべきカバレッジは「仕様カバレッジ」
- 自分たちが作っているソフトウェアで重要なのは「仕様を満たしているか」であってコードではない
- 無いよりはあった方がいいよね、程度
- 気にするくらいなら仕様を満たしているかどうかを考えろ
- やった方が良い
- 単体で本番と同様にSQLを発行して動作を確認している人はどれだけ居るか?
- 性能の問題だったりとか発覚したときにやりやすいよね
- やるんだったら、本番と同じDBを使うのが良い
- 使えるSQL違うし
- テスト用のスキームを作っておいた方が良い
- 並列実行可能性の保証
- やらない理由
- 遅くなるから
- 準備が面倒くさい
- TDDで出来上がってきた成果物をどれだけ信頼するのか?
- そのテストは本当に正しいと、どうやって保証するのか?
- 誰がそのテストをレビューするのか?
- ありがちなのは、TDDやってるくらい意識高い人なら良いんじゃね?って考え方
- TDDのプロセス監視
- 自動コミットさせて、Red-Green-Refactoringの切り替わりタイミングを見る
- ファイル変更検知→コンパイル→コンパイル成功であればテスト実行→結果(Red or Green)をコミットコメントにして自動コミット
- Redの時間が長い → タスクが大きすぎる
- RefactoringもしていないのにGreenの時間が長い → 何かが間違っている。変な進め方をしている。
- 手動コミットするのは、ブランチに対して何かをするときだけ