Booklog - 実践プロパティベーステスト PropEr と Erlang/Elixir ではじめよう
前から FsCheck を使いたいと考えてたが手探りすぎたのでなんか手引が欲しく、ちょうど良さそうな本が出たので積読してた。 少なくとも和書だと PBT について書いてるのがこの本しかないっぽいし。 はじめ PBT 基礎を習って、その後は演習等実践を通して理解を進めていくみたい。良さそうや。
2024-11-05, read count: 1, page: i ~ xii, 目次, 索引
第 1 章 プロパティベーステストの基礎。 プロパティベーステストは従来のテストと根本的にアプローチが異なる。 従来のテストは事例を並べるが、プロパティベーステストは「どのような入力を与えても常に同じであるような振る舞い」≒プロパティを定義する。 プロパティベーステストが適さないケースもあり、従来のテストより考えることが多くなり技能も必要だが、素晴らしい結果をもたらしてくれるって感じか。 事例ベースのテストは想像の範囲の仕様を記述するが、プロパティベーステストは想像の失敗をあぶり出す。そうそう、それこそ求めてるものよ。
2024-11-06, read count: 1, page: 1 ~ 11
第 1 章 プロパティベーステストの基礎。 従来のテストは単純なツールと多くのコード。 PBT は強力なツールと少しのコード化されたルール。 そのためツールの支援がなければ話にならんということで本で使うツールの紹介。 .NET もテストプロジェクトを分離するし FsCheck も同じ感じでやれそうやな。
2024-11-07, read count: 1, page: 11 ~ 18
第 2 章 プロパティを書く。 コード化されたルール、ジェネレータ、フレームワークが揃ってプロパティが得られる。 PBT には基礎的なステートレスプロパティとより複雑なステートフルプロパティがある。 あとは PropEr の使い方とか。後ほど FsCheck ではというあたりも自習しておく。
2024-11-08, read count: 1, page: 19 ~ 25
第 2 章 プロパティを書く。 備え付けジェネレータとコード化したルールを組み合わせてどテストを書くかってところ。 最初はサクッと書いてその結果からジェネレータを変える等プロパティを調整してく。 結構試行錯誤な印象。あとこの段階では組み込みのジェネレータ知らんことにはどうにもならなそう。 使うツールに習熟する必要あるって話の片鱗かな。
2024-11-09, read count: 1, page: 25 ~ 36
第 3 章 プロパティで考える。 優れたプロパティの実装は標準的なテストよりも難しいが、習熟するためのテクニックはある。 まずモデル化。単純で正しいと信用できる代替の実装。実行速度に問題があったとしても初手としては意味がある。 複数の実装パターンがない場合には従来のテストケースの汎化をする。どこまでを信頼するかは開発者の判断。 次に小さく分解した部分で常に真となるはずの不変条件を使う。単一の不変条件だと役に立たなくても他の部分の不変条件と組み合わせで信用を高められる。 テクニックがあるとはいえこの辺は実装しつつ習熟するしかないやろな。
2024-11-10, read count: 1, page: 36 ~ 46
第 3 章 プロパティで考える。 正順の処理に対して逆順の処理を書ける場合はそれが対称プロパティとなる。 対称プロパティそれだけでは整合性を保証するのみだが、不変条件を組み合わせることで強固なプロパティが得られる。 コツとして、複雑な処理をプロパティ 1 つだけで信頼性を高めようとせず、複数のプロパティに分けて段階的に検証していくのが望ましい。 この辺は従来のテストと同じかな。
2024-11-11, read count: 1, page: 46 ~ 53
第 4 章 カスタムジェネレーター。 まんべんなくランダムなデータでは発見できると限らない。 限られたエッジケースに焦点を絞るようなテストに最適なデータを、カスタムジェネレータで生成する。 まず統計情報をとりデフォルトジェネレータで生成された値がテストに十分か見極め、不十分ならカスタムジェネレータを利用する。 カスタムのデータ生成はそら欲しくなるよなという印象。
2024-11-12, read count: 1, page: 54 ~ 64
第 4 章 カスタムジェネレーター。 リサイズジェネレーター。生成される値の範囲を狭める。ただし狭めた分ばらつきが得られなくなる点に注意する。 変換ジェネレーター。データの生成と変換がジェネレーターとプロパティに分かれてしまうようなケースをジェネレーターにまとめられる。 またフィルタとして働く制約条件を設けるジェネレータもある。ただし除外されるデータの量が大量だと変換で賄う方が速いケースもあるので注意する。 生成・変換のいずれにしても効率的に欲しいデータを生成するには十分でなく、その場合確率を調整して狙いのデータに近づけることもできる。 ただし CSV や XML のような構造化データの生成には十分でないため更にテクニックがいる。 ここまできたら実際にテスト対象に必要なデータを試行錯誤するのが実践的で良さそうやな。勘所は練習しないとつかめなさげ。
2024-11-13, read count: 1, page: 65 ~ 76
第 4 章 カスタムジェネレーター。 再帰ジェネレーター。 繰り返しで作れるデータは概ね可能。 ただし正格評価で呼び出し階層が深くなりすぎる場合があり、そのときは遅延評価することで回避する。 確率的に再帰の終了条件を設定する場合、確率が固定されたり異常なサイズのデータが生成されることもある。 その場合繰り返し回数だけランダムに生成して、データの生成を通常の関数で記述することで軽くできる。 Erlang 力低くてピンとこんが正確評価は FsCheck も同じだし、他のエッセンスも似たようなもんだろうと推測する。
2024-11-14, read count: 1, page: 76 ~ 84
第 4 章 カスタムジェネレーター。 シンボリックコール。失敗したプロパティの出力が解読不能なバイト列などのデータ形式の場合に使える。 関数呼び出しやその引数をシンボルとして記録し、失敗時にその履歴が追跡できる感じ。 FsCheck にはシンボリックコールそのものはないみたい。それっぽいものを代替手段で実装することはできそうなので参考にはなるか。
2024-11-15, read count: 1, page: 84 ~ 92
第 5 章 信頼できるテスト。 これまで学んだ内容を実践する。 モデル化、事例テストの汎用化、不変条件、対象プロパティと事例テストによる固定化(anchor 錨)。 ここでは CSV のプロパティを書くためにまずジェネレータからかいてるが、仕様のコード化という点で TDD に通じる。 プロパティを定義するとその仕様の曖昧さが生み出すというのもいいな。 避けられない既知のバグを正解ケースとして、またプロパティの実装上避けられない暗黙の仕様のテストも事例テストが向いてる。 こういう事例テストの用途はリグレッションテストも該当する。
2024-11-16, read count: 1, page: 93 ~ 111
第 5 章 信頼できるテスト。 PBT のアイデアを使った事例テスト。 総当たりの範囲が狭く実行時間も許容できる場合は PBT より事例を列挙するほうが良いケースもある。 この場合原因の追跡のしやすさは劣るが、実例が網羅されるためランダム性は必要なくより信頼性も高くなる。
2024-11-17, read count: 1, page: 111 ~ 121
第 5 章 信頼できるテスト。 ココまでで出尽くしてるかのようで特に気づく点なかった。 FsCheck で同じような実習してみるのが練習になりそうやけどまだやってない。
2024-11-18, read count: 1, page: 121 ~ 136
第 6 章 プロパティ駆動開発。 TDD と同じように PDD する。 ポジティブテスト(テストがスべきことの検証)とネガティブテスト(プログラムが処理できないことのテスト)の技法を探っていく。 最初に簡単なプロパティを書き、それを green にするために最小実装、実装後統計を見て妥当性のチェック。 プロパティ書いてジェネレータを書いて実装してって流れは F# だとコンパイルエラーうざそうで先に空実装しちゃいそう。 TDD もなんか苦手やし。
2024-11-19, read count: 1, page: 137 ~ 144
第 6 章 プロパティ駆動開発。 1つ目のプロパティでカバーできなかったケースのテストを新たに作る。複雑化しないように小分けにする。 これはジェネレーターも同じで、テストしたいパターンを構成する最小の要素のジェネレーターを作る。 それらを組み合わせて複雑なパターンを網羅する。 ここまでは想像の範囲で期待の挙動をするかテストするポジティブテスト(ハッピーテスト)。次節からネガティブテスト。 単純で本実装とは違う形で実装ってのが結構むずそうよなーやり方は色々あるってのはわかるんやけどバイアスかかりそう。
2024-11-20, read count: 1, page: 144 ~ 156
第 6 章 プロパティ駆動開発。 ネガティブテスト。不測のケースを探るのには広範囲を対称とする曖昧なプロパティが適している。 完全にランダムだと満遍なく事例をカバーしにくいので、意図的に予測可能なデータを混ぜていき、ばらつきが狙い通りかは統計情報で評価していく。 またポジティブテストの制約を緩めることで不測のケースを探索するのも良い。 この不測のケースの中には動的型付け言語だから起こり得るものがあり、静的型付け言語の場合だと厳しく型で制約しているから起こり得ないものもある。 とはいえちょいちょい制約子忘れるのでそういった漏れを発見できるかもしれないのは大きそうな。
2024-11-21, read count: 1, page: 156 ~ 169
第 7 章 収縮。
失敗したケースを再現する最も簡単な形を探す機構が収縮(shrinking)。
最も簡単な形はジェネレーターにとってのゼロ点、概ね空を指す値だったり中間点を指す。
特殊なケースは例えば初期位置が定まっているデータ。 Unix タイムスタンプであれば 1970 年 1 月 1 日。
このようなデータ構造が複雑な場合は簡単な形が得られないこともあり、その場合は収縮機能を制御する機構を使う。
PropEr はマクロだけど FsCheck だと shrinker という seq
を返す関数で表現してるみたい(まだ試してない)
2024-11-22, read count: 1, page: 170 ~ 184
第 8 章 標的型プロパティ。 通常のプロパティは各ケースが独立しているが標的型プロパティは後続のケースのデータ生成に影響を与えることができる。 それにより特別なジェネレータを作ることなく標準的なジェネレータで狙ったデータを生成できる。 標準的型プロパティは焼きなまし法(Simulated Annealing)という統計的な手法で値を探す。 初期は山登り法(Hill Climbibg)が利用された。元は探索マクロとして追加された機能。 PropEr の進んだ昨日だけあって FsCheck には Targeted Property なさそうなのでそういう手法もあるのかという程度の理解。
2024-11-23, read count: 1, page: 185 ~ 192
第 8 章 標的型プロパティ。 標的型プロパティで狙いたいのはエッジケース、例えば木構造が片方に偏ってるとかがあってる。 焼きなまし法の計算時間で明らかに通常のジェネレータより遅くなる。 独自に近傍関数を定義することでより効果的な選択をさせることができる。 ただしカスタムジェネレータで狙いのデータを生成するのと同様、ランダムさは狭まる。 このことからも満遍なく多様なデータのプロパティとは分けて考えるべきところなんかな。
2024-11-24, read count: 1, page: 192 ~ 200
第 8 章 標的型プロパティ。 標的型プロパティの独創的な使い方として通常のプロパティではできない使い方がある。 例えば計算量が爆発して実行時間が伸びるようなケースは、実行時間が最大化する方向にでー他を探す必要があり、通常脳プロパティではできない。 標的型プロパティは仕様やロジックの穴を探すのに特に光るものがある印象。
2024-11-25, read count: 1, page: 200 ~ 204
第 9 章 ステートフルプロパティ。
ステートフルプロパティが有効なのは「コードが何をすべきかは単純だが実装が複雑」なパターン。従来の結合テストやシステムテストが該当する。
形式手法未満のモデル検査の亜種といえる(らしいがわからん)。
モデル・コマンドのジェネレーター・実際のシステムの 3 つのパーツで成り立つ。
システムの振る舞いモデルと検証対象のシステムに、検証対象の操作をあらわす前提条件を与え、モデルと検証対象のシステムを事後条件により確かめる。みたいな感じか。
こういう仕組みは、自動じゃないけど今 pocof の SelectPocofCommand
にやってるテストと同じ感じなので、熟達したらかなりイケそうな気がしてきた。
FsCheck 的には Model-based Testing なんかな。
2024-11-26, read count: 1, page: 205 ~ 212
第 9 章 ステートフルプロパティ。 生成したテンプレートどの部分に何を書かなければならないか。 実行するコマンドを生成する抽象的なモデルの段階と、実際のシステムを実行する現実の段階の両方で利用される関数がある。 そのためモデルに対する副作用を避けなければならない、とか。 Model-based Testing でも同じなんかな(特に調べた訳では無い)。
2024-11-27, read count: 1, page: 212 ~ 217
第 9 章 ステートフルプロパティ。 モデルの構築は利用者(運用者)の気持ちでメンタルモデルを構築するところから。 ここでは有効期限がないキャッシュのプロパティのモデルとして FIFO リストを利用している。 ステートレスプロパティ同様より単純な同じ振る舞いをするものを探し出して使うイメージか。
2024-11-28, read count: 1, page: 217 ~ 230
第 9 章 ステートフルプロパティ。 シーケンシャルな処理を自動的に並列実行に変換してテストしてくれる。その例。 FsCheck では並列実行する機能はあるけど自動で並列テスト化する機能はないような... 最先端はこういうことができるというレベルの認識で留めておくか。
2024-12-02, read count: 1, page: 231 ~ 240
第 10 章 ケーススタディ:書籍の貸し出しシステム。 現実のシステムのステートフルプロパティでテストする。 大局的な視点からはじめ、徐々に対称を絞り込み、最終的に決定論的で厳格なプロパティにする。 テスト対象となるコードの実装の節なので特になし。
2024-12-03, read count: 1, page: 241 ~ 252
第 10 章 ケーススタディ:書籍の貸し出しシステム。 大規模なシステムの検証はシステムに対して有効・無効なデータを把握するところから始まる。 まず広範囲なテストから始める。エラーの中にはステートレスプロパティが得意とする検査が引っかかることもあり、その場合は調査のためにステートレスプロパティを書くこともある。 早い話が E2E test なので sample のような DB 伴うものは TestContainers でも使わんとこれはめんどくさそうやな。
2024-12-04, read count: 1, page: 252 ~ 259
第 10 章 ケーススタディ:書籍の貸し出しシステム。 モデルを使ったテストをする方法として shim (詰め木)と呼ばれる通常の操作を wrap するモジュールを使う。 shim を使ってプロパティを組み立てる。状態に依存する処理とそうでない処理のコマンドの生成は分ける。 shim 、前提条件、状態を進める処理、事後条件の定義は繰り返しが多くなりがち。 それは簡潔さと自明であることの良い兆候である。 決定論的なモデルを記述するのが難しかったり、事後条件で登録したデータを検証しにくかったりする場合、 それはシステムの可観測性など開発の方法を見直す機会しれないというシグナルであると。
2024-12-10, read count: 1, page: 260 ~ 270
第 10 章 ケーススタディ:書籍の貸し出しシステム。 わざわざ失敗するようテストを書いてリリース前にバグを「減らす」(時間とともにバグは必ず見つかる)。 また PBT は確率的なテストなためプロパティ再実行のたびに違うエラーが見つかることもある。 モデルによっては実際のシステムが受け付けるパターンと違う場合がある(例えばエスケープが必要な文字)。 その場合はプロパティで検証したいことによってプロパティを分けるべき。
2024-12-11, read count: 1, page: 270 ~ 278
第 10 章 ケーススタディ:書籍の貸し出しシステム。 前提条件によって収縮が失敗してしまう。前提条件は本物の bug に対してのみ機能するように調整がいる。 PropEr では同じモデルを使って並列テストも実行できる。それがこのステートフルプロパティのサニティチェックになる。
2024-12-15, read count: 1, page: 278 ~ 284
第 11 章 有限状態機械プロパティ
PropEr では取りうる状態が 1 つでそれがイベントにより遷移していく有限状態機械のモデル化に特化したプロパティがある。
ステートフルプロパティと似ているが、状態の組み合わせが爆発する(前提条件で大幅にパターンを絞らないければならない)ような場合、
各状態に名前をつけることができ利用者がそれを判別できるようなケースに向いている。
例題として circuit_breaker
というライブラリを対象にした例が挙げられている。
この場合の取りうる状態は、 ok(遮断されておらずシステムが有効な状態), tripped(障害が多発しシステムが遮断された状態),
blocked(利用者が操作を止めるために意図的にサーキットブレーカーを落とした状態) があって正に状態機械向きである。
2024-12-26, read count: 1, page: 285 ~ 297
第 11 章 有限状態機械プロパティ サーキットブレーカーのモデルを書いて FSM(有限状態機械) プロパティを書いていく。 テスト対象の挙動を理解してないとかけないとか、テスト結果の統計情報からテストが期待した分布になるよう重み付けを調整したりとか、これまでの章で学んだことを活かしている。 PropEr では~という話なので FsCheck でどうしよう...というところではあるが、この辺りの感覚は共通して参考にできる。 付録は飛ばした。これでこの本は終わり。あとは本でシミュレートしたのを実践してかないと。
2024-12-27, read count: 1, page: 297 ~ 314
Years
Books
- Domain Modeling Made Functional 関数型ドメインモデリング ドメイン駆動設計と F# でソフトウェアの複雑さに立ち向かおう
- People Powered 「ビジネス」「ブランド」「チーム」を変革するコミュニティの原則 遠くへ行きたければ、みんなで行け
- ピアリング戦記 日本のインターネットを繋ぐ技術者たち
- プログラマーのための CPU 入門 CPU は如何にしてソフトウェアを高速に実行するか
- プログラマー脳 優れたプログラマーになるための認知科学に基づくアプローチ
- プログラミング F#
- ユーザーの問題解決とプロダクトの成功を導く エンジニアのためのドキュメントライティング
- 世界一流エンジニアの思考法
- 入門・倫理学
- 実践プロパティベーステスト PropEr と Erlang/Elixir ではじめよう
- 本を読む本
- 男のスコッチウィスキー講座 100 蒸留所 巡礼試飲旅
- 魏武注孫子