Zenn
Open26

『実践プロパティベーステスト』 読書会

mohiramohira

README

公式情報

『実践プロパティベーステスト ― PropErとErlang/Elixirではじめよう』

訳書の正誤表はないっぽい?

原著

https://pragprog.com/titles/fhproper/property-based-testing-with-proper-erlang-and-elixir/

サンプルコードや正誤表がある。

読書会の進め方

2024-01-11 『実践プロパティベーステスト』読書会 README

mohiraの勉強用リポジトリ

https://github.com/mohira/jissen-pbt

関連する参考文献

全般

mohiramohira

2024/01/11(木) 20:00

今日の目標

  • 読書会を実施する
  • サンプルコードを実行できるようにする ≒ 1章を読む感じ
  • 参加しているみなさんそれぞれの期待を知る(そんで、書く)

今日のひとこと

出だし好調

実績

  • 表紙から「p.5 1.1 プロパティベーステストの約束」の直前まで。

Discord開始リンク

次はどこから?

  • 次回: p.5 1.1 プロパティベーステストの約束 から

最近の話

  • ドキュメント化を上手に頑張りたい話。テキストこそ最強。
  • AC6! ランクマも落ち着いて、縛りプレイもやったり。Sランク昇格には勝率8割くらい必要らしい。
  • Gmailのガイドラインに対応するやつ
  • ISUCONの練習をしてみた話。KOBA789さんのライブ配信おもろいよね。
  • 風邪治った!
  • React! React! React! と、Hotwire
  • RustのORM sea-orm
  • 『アウトレイジ』はめっちゃみてるけど、『首』は...!!!
  • 物理を学ぶ。微分方程式〜〜〜。
  • 『Leading Quality』

学習内容やら発見やら何やら

めっちゃ期待感はアガる!

PBTへの期待

  • 勘違いしてたら、そもそもテストケースやらバグもみつけられん問題の対処。
  • テストケースをだらだら羅列しなくて済む

PropErの発音は「ぷろぱー」でよさそう

著者がこの本について語ってる講演っぽいのを見つけました。
75秒時点で本のタイトルを言ってるんですが、「ぷろぱー」で合ってそうです
https://youtu.be/LvFs33-1Tbo?feature=shared&t=75
https://discord.com/channels/508093437187457045/1190292563907838053/1194994418982326302

とはいえ、PBTの弱点やらEBTの違いやらは気になる...(積極的にPBTを即採用、とは、まだ、ならない)

いくつかのツッコミ

  • EBTとの使い分けは?
  • 自作自演になりそうでは?
  • テストに自信を持てるとして、その根拠は何なのか?
  • 仕組みも知りたいね。
  • いいことづくめっぽいけど、PBTの限界や、EBTに対するデメリットを知りたい
  • 入力値の自動生成はどうなんだ?

プロパティベースとテストの「プロパティ」って何?

きっとわかるらしい。

なんとなく、数学っぽさを感じつつ。

FORALLは、jqwickでも登場する命令だった。

その他

  • 「筆致」という日本語がかっこいいのでいくつか上手に使いたい
  • 「新しい問題空間を探索する」
  • この本が、現時点で、最強。やるしかない。
  • 「自信」とか「確信」ってことば、めっちゃでてくるな

著者は"わかっている"

読者への宿題と言ってヒントも何も示さない演習は煩わしいだけなので、すべての演習には解答を用意してあります。

ほんと、著者はわかっている

mohiramohira

2024/01/25(木) 20:00

今日の目標

  • 22:00におわる(早く寝たいから)

今日のひとこと

これが「プロパティで考える」ということだ!!!(ライバルのセリフ)

実績

  • 「p.5 1.1 プロパティベーステストの約束」から「1章読み切り」まで

Discord開始リンク

次はどこから?

  • 次回: p.19 2章 プロパティを書く から

最近の話

人力では無理な17連コンボと33連コンボでバグを発見するPBTの威力

データベースを破損させる 17段階の手順と33段階の手順を発見しました。どれほど懸命に探しても、33 段階もの手順を正しく 踏んで初めてデータベースが破損するような問題に気がつく

  • 行数とかに関係なくスゴイ
  • 1ストーリー33手順は用意するの辛そう
  • ただし、PBTが貢献しているとして、どこからどこまで? とか、PBTだからこそなのか? などなど
  • 鬼の33連コンボはどうなってるんだろう。まじで。

プロパティとは「実行可能なコード」

そう捉えると読みやすい。 というか、そう書いてある。

お釣りのルールの2つ目をちゃんと言語化できるのスゴイし、それがPBTではより大事そう!

お釣りとして渡す紙幣と硬貨は、額面が最大の紙幣から額面が最小の硬貨になる まで、徐々に小さい額面にしていく。言い換えると、客に手渡す紙幣と硬貨をで きる限り少なくすること、と定義できる

これね。

そして、これ。

プロパティベーステストで難しいのは、プログラムの振る舞いに関する抽象的な着 想を、コードとして表現された特定のルールに落とし込む方法を見つけるところ

プロパティベーステストは「多くのことを考えさせられる」

実際、勉強会の議論もそうだったの最高!

現実のシステムではどうか? とか、「いや、そもそも前提として対応不要でしょ」とか「客の支払い自体に制約をつけたら?」などなど。

EBTでも同じ議論は可能だけど、PBTでは、よりダイレクトに考える機会が得られる印象を抱きましたね。

「想定通りできているかを確認するEBT」 vs 「想定自体が正しいかどうかを調べるPBT」だと思った

事例ベーステストは「コードが自分たちの想定した通りに実 行されるかを確信する」ための助けになり、
プロパティベーステストは「プログラム に何ができて何ができないかを確認するためにその振る舞いを探索し、そもそも自分 たちの想定が正しいかどうかを判断する」ための助けになります。

想像を超える、だとか勘違いを発見するみたいなのがPBTに向いているというかそういう感じ。

EBTは、実装を確認するには有効。うっかり「(悪い意味での)現状の追認」にならないように。

環境構築成功!

やったぜ!

その他

  • Q. 入力値の生成のときに制約はつけられるのかな? (つけられそうではあるけど、謎)
  • Q. PBTはなんとなく、異常系と正常系の区別をしない? 気がしないでもない?
  • 今日の漢字 「宥める」(なだめる)。ちなみに前回は「筆致」
mohiramohira

2024/02/08(木) 20:00

今日の目標

  • 参加する(遅刻しそうだったので)

今日のひとこと

プロパティベーステスト、おもしろくなってきたな!

functionジェネレーターのすごさたるやっていうか、それに気づいた感動。

実績

  • 2章 から 2.4 まとめ の前 まで

Discord開始リンク

次はどこから?

  • 次回: p.34 2.4 まとめ から

最近の話

  • 食生活の乱れ
  • やはりAC6! ランクマ月末にリセット + Updateもあるよ(運営、だいじょうぶか?)
  • AC6、TAやる? RTAもいく? PS5の録画時間制限が1h
  • 最近、めちゃ早起き。
  • Gmailガイドラインの影響はまだまだ
  • 犬の散歩で外に出る
  • copilotさんでもサポートできない昔のコード群
  • ローコンテクストとハイコンテクストのバランス問題(わかる)
  • 「let多相」を攻略した話
こんな感じで関数を多相的に扱うのは単純型付きラムダ計算ではできない
(λf. if f true then f 0 else 1) (λx. x)

f の型は A -> Aになるが、f trueとf 0を推論する時に、
制約がA = Bool A = Natになってうまくいかない

let多相を許すように拡張すると(let式を使った時だけ)↓が許されるようになる
let f = λx. x in if f true then f 0 else 1
fの型は変わらずA -> Aになるが、f trueやf 0の型を推論する時に、
fの型に独立した別の型変数をその都度割り当てて B -> BやC -> Cとして扱うようにする
そうすると、制約がB = Bool C = Natになって問題なくなる 

プロパティの肝となるのは「常に真になるようなプログラムのルー ルを見つけ出すこと」にあります

それな! めっちゃムズい!

functionジェネレーターのすごさ

  • 高階関数のテストに使えるやつだ。
  • 関数もデータだから、生成対象に含まれてしかるべきってのは確かにそう
  • 同じ引数なら結果はおなじになる(再現できないからだと思う)
  • 引数を変えたらそりゃ変わる
%% 関数も生成できちゃう
{ok,F} = proper_gen:pick(proper_types:function(3, proper_types:list())).
2> F(1,2, three)
   .
['[\ryGl\212\032¶è',
 <<221,116,39,40,23>>,
 20.331976709882383,
 ['@Ò¨£¢í','ëÓy\223Zóîµ',-6.063978933545364,
  [{-5.603717806674451,{[],{{}},'i^GIDø'}}],
  5,{},
  [{}]],
 '\025Ð \037g',
 [-4.413810932277308,-4,-7,'?',
  [-15,'Éç\022\223y\0309',[{{},2}],{},[],'5»'],
  '',{}]]
3> F(1,2, three).
['[\ryGl\212\032¶è',
 <<221,116,39,40,23>>,
 20.331976709882383,
 ['@Ò¨£¢í','ëÓy\223Zóîµ',-6.063978933545364,
  [{-5.603717806674451,{[],{{}},'i^GIDø'}}],
  5,{},
  [{}]],
 '\025Ð \037g',
 [-4.413810932277308,-4,-7,'?',
  [-15,'Éç\022\223y\0309',[{{},2}],{},[],'5»'],
  '',{}]]
% 引数を変えたらそりゃ変わる
5> F(1,2, four).
[{'\031',-31.57985196624301,9.891533111565897,'','Ý',
         '\214\203¦Í«Û\035\v'},
 [],-23.588535868412997]
6> F(1,2, four).
[{'\031',-31.57985196624301,9.891533111565897,'','Ý',
         '\214\203¦Í«Û\035\v'},
 [],-23.588535868412997]

ジェネレーターすごい

ジェネレーターが劣っているフレームワークに魅力はありません

ジェネレートしている感があるな

いい感じのデータを生成してくれる

「いい感じ」という言葉が最もフィットする文脈かもしれん。PBTのGeneratorにおいては。

「プロパティを実行する」という語彙

おさらい) プロパティは、実行可能なコードだからな

Shrinkingはすごいし、実際に"落ちたシーン"がTraceBackに出るのがめちゃいい

"落ちたシーン" → 実際に失敗したときの関数および引数の具体的な値

ebi_chanebi_chan

2024/02/22(木) 20:00

今日の目標

決めてなかった

今日のひとこと

書いてあることは理解できた気がするけど、まだテストしたい関数のプロパティがポンポン思いつく自信はない。もっと練習問題欲しい

実績

  • 2章 2.4 まとめ から 3章 3.7 演習 の前まで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1210181629851734097

次はどこから?

  • 次回: p.51 3.7 演習 から

最近の話

そういえばしてなかった。
「例外やエラーの分類」とかLinuxやOSとかの低レイヤーの話をしてた気がする

2.5 の演習

問題なく正解(コードを読んで予想を立てたり、 io:format を挟んで確認したりした)

ややこしい状況で十分に適切なプロパティを書くうえで役立つ秘訣

  • モデル化
  • 事例テストを汎化する
  • 不変条件
  • 対称プロパティ

モデル化

モデル = テスト対象より非常に単純な実装(たいていはアルゴリズム的に非効率的なコード)

「システムが何をするか」は複雑だけど「ユー ザーが何を知覚するか」は単純であるような、副作用や依存関係が多いステートフル システムの結合テストもあります
システムのDBをインメモリの連想配列に置き換えたり

稀に利用できる素晴らしいモデル化の一種として、神託(oracle)と呼ば れるものがあります
画像処理ライブラリをテストする時にOpenCVと比較するとか

事例テストを汎化する

事例テストの入力と期待される出力の組をジェネレータを組み合わせて作る感じ

不変条件

教科書の例だと、重複した要素同士の個数が入れ替わった時に気付けない!

[3, 1, 2, 2, 3, 3]

というListをsortした結果が

[1, 2, 2, 2, 3, 3]

になっていてもテストを通ってしまう。すごい良い気付き

sunakansunakan

2024/03/07(木) 20:00-22:00(+01:00)

PBT勉強会-04

今日の目標

今日のひとこと

みんなで問題をわいわい・つっつきながら解くのは、とても楽しい。(問題文にいちゃもんつけたりしてる)

実績

3章の演習全部

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1215254141769748520

次はどこから?

4章から

最近の話

  • AC用のツール(Node.js)でPBT実践。EBTとの使い分けがわかってきたかも
  • 巨大なJSONを表現するのに、PBT良さそう
  • ブロックチェーンのハッカソン
  • 過去にサッカーチーム(ベガルタ)がNFTアート売ってた
  • サッカー観戦楽しい!
  • 情報セキュリティの敗北史

最後の感想共有

  • PBTではなく、Erlangに詳しくなる生活
  • 問題文、もっと丁寧に書いて
  • 標準関数を使うことで、プロパティの検証ができるとおもっていたけど、その標準関数の仕様を見誤るとドツボ、という学び。
  • 振り返りあると嬉しい

追記1:やったことのまとめ・自分の解釈

問題1

あまり言うこと無し
3つではなくて、4つでいい

問題2

注意書きが欲しかった(以下は例)

※ただし、ある程度固定して、小さいプロパティで良い。(完璧なプロパティを1つ書く必要はない)

ただ、変数をある程度固定しても小さいプロパティであって、プロパティであることは変わりないなと上記記述して考え直した

「プロパティを考える」という時、1つの大きなものを考える必要はない

Q. どういう類の問題だったか?

A.
prop_sort()が「3.3 不変条件」の文脈で触られている
なのでこの問題は

  • モデル化
  • 事例テストの汎化
  • 小さい不変条件をたくさん見つける
  • 対称プロパティ

のうち、「小さい不変条件をたくさん見つける」と言えそう

問題3

(Discord見ても)20~25分くらいかけたと思う

  • SetとListの重複の扱いについて知っていること
  • Setに順番は無いため、Listにすると順番は保証されていないこと

Q. どういう類の問題だったか?

A.
問題冒頭で、「モデルを使って」と記述されている
なのでこの問題は

  • モデル化
  • 事例テストの汎化
  • 小さい不変条件をたくさん見つける
  • 対称プロパティ

のうち、「モデル化」と言えそう

問題4

  • ここにいっぱい時間かけたように感じた
  • 実際は30~40分くらいかもしれない
  • 問題1個にそれだけでも長いが、次が長すぎた

自分が問題文を考えるなら

2つの辞書をマージするようなdict_mergeを実装した
dict_mergeは3つの引数を取る
第1引数はキーが重複した時のハンドリングをする関数をとる
第2,3引数はマージしたい2つの辞書をとる

dict_mergeのプロパティを考えたい
以下のプロパティはdict_mergeは衝突しないようにマージされ、各キーが存在していることを比較することで、各要素が1つしか存在しないことを検証している
しかし、このプロパティだけでは不十分である
何が足りていないか?
また、どのような修正or追加をしたら良さそうか?

Q. どういう類の問題だったか?

A.
参考解答から考えると

  • モデル化
  • 事例テストの汎化
  • 小さい不変条件をたくさん見つける
  • 対称プロパティ

のうち、問題の方の検証は「小さい不変条件をたくさん見つける」だけど、参考解答の方で必要になるのは「事例テストの汎化」って言えそう。。?

問題5

(Discord見ると)1.5時間位使ってる?

Q. どういう類の問題だったか?

A.
参考解答から考えると、2つの異なる実装で比較している
そのため、この問題は

  • モデル化
  • 事例テストの汎化
  • 小さい不変条件をたくさん見つける
  • 対称プロパティ

のうち、「モデル化」と言えそう

mohiramohira

Erlangの内包表記

内包表記とわかればスルッと読める
(|| を OR と解釈しがち)

%% Erlang
[1 || Char <- Stripped, Char =:= $\s]
# Python
>>> stripped = "a b a"
>>> [ 1 for char in stripeed if  char == " "]
[1, 1]
>>> [ 1 for char in stripped if  char == "b"]
[1]

Erlangの文字列

$ rebar3 shell
===> Verifying dependencies...
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]

Eshell V14.2.1 (press Ctrl+G to abort, type help(). for help)
1> [32].
" "
2> [9].
"\t"
3> [64].
"@"
4> [65].
"A"
5> [66].
"B"
6> [255].
"ÿ"
7> [256].
[256]
ebi_chanebi_chan

2024/03/21(木) 20:00-22:00(+01:00)

実績

4章の初めから4.3.1の途中(p68, Sizedマクロを使ったところ)まで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1220328379161641000

次はどこから?

4.3.1 リサイズジェネレーターから

最近の話

  • Python全然書いたことないけどレビューが回ってきた
  • 年度末で評価の時期。めんどくさい
  • バファリンの「半分の優しさ」は胃の保護だった
  • 引き続きACのツール開発中。PBTの理解もより深まってきた
  • 骨髄バンクのドナー候補者に選ばれたけどドナーにはなれなかった
  • Lean で証明付きの評価器を書いた話。関数の性質について証明するのはPBTとちょっと似てる
  • GitLab本、読了!エッセンスだけ取り入れるのは難しそう

今回の感想

  • サイズに関する実験が勉強になった、やって良かった
  • collect/2, aggregate/2 の有用さが分かった

メモ

最後にやった実験について

検証の動機:「このSIZEDマクロのSizeが 0 の時ってジェネレータはどんな値を生成するんだろう?」が知りたかった
色々試行錯誤した結果、「0の時じゃなくて毎回出せば良くない?」となった
最終的に実行したのはこういうコード

prop_profile1() ->
    ?FORALL(Profile, [{name, string()},
        {age, ?SIZED(Size, choose(Size,Size))},
        {bio, string()}],
        begin
            ?debugFmt("HELLO~p~n", [Profile]),
            NameLen = to_range(10,length(proplists:get_value(name, Profile))),
            BioLen = to_range(300,length(proplists:get_value(bio, Profile))),
            aggregate([{name, NameLen}, {bio, BioLen}], true)
        end).

観察の結果

  • サイズは最初 1 から始まる
  • 試行回数が増えるごとに段々増えていく
    • 試行回数が100の時は2~3回ごとに1増える感じ
    • 試行回数が10の時は1ずつじゃなくてもっと飛ばし飛ばしで増えていた
  • サイズ はジェネレータが生成するデータの上限を決めている
    • 例: リストを生成するジェネレータの場合、サイズが1の時は要素数が0か1になる
sunakansunakan

Sizeの増え方や他Generatorの挙動について、もう少し深堀りしました

  • n=100でもn=1万でもsize=42くらいまでしか伸びず、興味深かったです
    • 何度も同じsizeが使われてそうです

検証コード

prop_check_size() ->
    ?FORALL(
        Profile,
        [
            {size, ?SIZED(Size, choose(Size, Size))},
            {name, string()},
            {bio, string()},
            {age, integer()},
            {tags, list(integer())}
        ],
        begin
            Profile2 = [
                {size, proplists:get_value(size, Profile)},
                {name_len, length(proplists:get_value(name, Profile))},
                {bio_len, length(proplists:get_value(bio, Profile))},
                {tags_len, length(proplists:get_value(tags, Profile))}
            ],
            ?debugFmt("Profile2: ~p~n", [Profile2]),
            true
        end).

実行コマンド

rebar3 proper -m モジュール名(ファイル名) -n 〇〇
結果(sizeの増え方と、各種lenに注目)
----[n=10]
Profile2: [{size,1},{name_len,1},{bio_len,1},{tags_len,0}]
Profile2: [{size,5},{name_len,3},{bio_len,2},{tags_len,3}]
Profile2: [{size,9},{name_len,3},{bio_len,7},{tags_len,5}]
Profile2: [{size,13},{name_len,4},{bio_len,13},{tags_len,1}]
Profile2: [{size,17},{name_len,17},{bio_len,3},{tags_len,17}]
Profile2: [{size,21},{name_len,11},{bio_len,10},{tags_len,4}]
Profile2: [{size,25},{name_len,20},{bio_len,13},{tags_len,1}]
Profile2: [{size,29},{name_len,6},{bio_len,29},{tags_len,28}]
Profile2: [{size,33},{name_len,13},{bio_len,14},{tags_len,14}]
Profile2: [{size,37},{name_len,18},{bio_len,22},{tags_len,11}]

----[n=20]
Profile2: [{size,1},{name_len,1},{bio_len,1},{tags_len,0}]
Profile2: [{size,3},{name_len,0},{bio_len,0},{tags_len,0}]
Profile2: [{size,5},{name_len,3},{bio_len,4},{tags_len,4}]
Profile2: [{size,7},{name_len,5},{bio_len,3},{tags_len,6}]
Profile2: [{size,9},{name_len,2},{bio_len,5},{tags_len,2}]
Profile2: [{size,11},{name_len,11},{bio_len,0},{tags_len,8}]
Profile2: [{size,13},{name_len,8},{bio_len,6},{tags_len,1}]
Profile2: [{size,15},{name_len,4},{bio_len,1},{tags_len,0}]
Profile2: [{size,17},{name_len,15},{bio_len,3},{tags_len,11}]
Profile2: [{size,19},{name_len,0},{bio_len,10},{tags_len,3}]
Profile2: [{size,21},{name_len,15},{bio_len,10},{tags_len,14}]
Profile2: [{size,23},{name_len,2},{bio_len,13},{tags_len,18}]
Profile2: [{size,25},{name_len,9},{bio_len,15},{tags_len,16}]
Profile2: [{size,27},{name_len,27},{bio_len,23},{tags_len,10}]
Profile2: [{size,29},{name_len,15},{bio_len,21},{tags_len,4}]
Profile2: [{size,31},{name_len,1},{bio_len,14},{tags_len,25}]
Profile2: [{size,33},{name_len,10},{bio_len,20},{tags_len,30}]
Profile2: [{size,35},{name_len,3},{bio_len,23},{tags_len,15}]
Profile2: [{size,37},{name_len,7},{bio_len,20},{tags_len,28}]
Profile2: [{size,39},{name_len,15},{bio_len,2},{tags_len,22}]

----[n=100]
Profile2: [{size,1},{name_len,1},{bio_len,0},{tags_len,1}]
Profile2: [{size,1},{name_len,1},{bio_len,1},{tags_len,0}]
Profile2: [{size,1},{name_len,1},{bio_len,0},{tags_len,0}]
Profile2: [{size,2},{name_len,1},{bio_len,0},{tags_len,1}]
Profile2: [{size,2},{name_len,2},{bio_len,2},{tags_len,1}]
Profile2: [{size,2},{name_len,0},{bio_len,2},{tags_len,1}]
Profile2: [{size,3},{name_len,3},{bio_len,0},{tags_len,0}]
Profile2: [{size,3},{name_len,1},{bio_len,3},{tags_len,3}]
Profile2: [{size,3},{name_len,1},{bio_len,0},{tags_len,3}]
Profile2: [{size,4},{name_len,3},{bio_len,3},{tags_len,4}]
Profile2: [{size,4},{name_len,0},{bio_len,2},{tags_len,0}]
Profile2: [{size,4},{name_len,4},{bio_len,3},{tags_len,3}]
Profile2: [{size,5},{name_len,2},{bio_len,0},{tags_len,5}]
Profile2: [{size,5},{name_len,5},{bio_len,0},{tags_len,4}]
Profile2: [{size,5},{name_len,2},{bio_len,1},{tags_len,0}]
Profile2: [{size,6},{name_len,4},{bio_len,2},{tags_len,5}]
Profile2: [{size,6},{name_len,1},{bio_len,3},{tags_len,3}]
...
Profile2: [{size,36},{name_len,28},{bio_len,27},{tags_len,33}]
Profile2: [{size,36},{name_len,25},{bio_len,2},{tags_len,10}]
Profile2: [{size,37},{name_len,15},{bio_len,15},{tags_len,2}]
Profile2: [{size,37},{name_len,23},{bio_len,33},{tags_len,30}]
Profile2: [{size,38},{name_len,7},{bio_len,29},{tags_len,4}]
Profile2: [{size,38},{name_len,30},{bio_len,24},{tags_len,34}]
Profile2: [{size,39},{name_len,31},{bio_len,19},{tags_len,9}]
Profile2: [{size,39},{name_len,17},{bio_len,17},{tags_len,8}]
Profile2: [{size,40},{name_len,5},{bio_len,35},{tags_len,6}]
Profile2: [{size,40},{name_len,8},{bio_len,2},{tags_len,1}]
Profile2: [{size,41},{name_len,26},{bio_len,39},{tags_len,0}]
Profile2: [{size,41},{name_len,1},{bio_len,10},{tags_len,28}]
Profile2: [{size,42},{name_len,41},{bio_len,26},{tags_len,4}]
Profile2: [{size,42},{name_len,33},{bio_len,36},{tags_len,5}]

今回学んだcollectを使う

今回学んだcollectを使って、Sizeをメトリクス値として統計取ってみる

prop_check_size() ->
    ?FORALL(
        Profile,
        [
            {size, ?SIZED(Size, choose(Size, Size))}
        ],
        begin
            collect(proplists:get_value(size, Profile), true)
        end).
結果
----[n=100]
 3.00% 1
 3.00% 2
 3.00% 3
 3.00% 4
 3.00% 5
 3.00% 6
 3.00% 7
 3.00% 8
 3.00% 9
 3.00% 10
 3.00% 11
 3.00% 12
 3.00% 13
 3.00% 14
 3.00% 15
 3.00% 16
 2.00% 17
 2.00% 18
 2.00% 19
 2.00% 20
 2.00% 21
 2.00% 22
 2.00% 23
 2.00% 24
 2.00% 25
 2.00% 26
 2.00% 27
 2.00% 28
 2.00% 29
 2.00% 30
 2.00% 31
 2.00% 32
 2.00% 33
 2.00% 34
 2.00% 35
 2.00% 36
 2.00% 37
 2.00% 38
 2.00% 39
 2.00% 40
 2.00% 41
 2.00% 42

----[n=1000]
 2.40% 1
 2.40% 2
 2.40% 3
 2.40% 4
 2.40% 5
 2.40% 6
 2.40% 7
 2.40% 8
 2.40% 9
 2.40% 10
 2.40% 11
 2.40% 12
 2.40% 13
 2.40% 14
 2.40% 15
 2.40% 16
 2.40% 17
 2.40% 18
 2.40% 19
 2.40% 20
 2.40% 21
 2.40% 22
 2.40% 23
 2.40% 24
 2.40% 25
 2.40% 26
 2.40% 27
 2.40% 28
 2.40% 29
 2.40% 30
 2.40% 31
 2.40% 32
 2.40% 33
 2.40% 34
 2.30% 35
 2.30% 36
 2.30% 37
 2.30% 38
 2.30% 39
 2.30% 40
 2.30% 41
 2.30% 42

----[n=10000]
 2.39% 1
 2.39% 2
 2.39% 3
 2.39% 4
 2.38% 5
 2.38% 6
 2.38% 7
 2.38% 8
 2.38% 9
 2.38% 10
 2.38% 11
 2.38% 12
 2.38% 13
 2.38% 14
 2.38% 15
 2.38% 16
 2.38% 17
 2.38% 18
 2.38% 19
 2.38% 20
 2.38% 21
 2.38% 22
 2.38% 23
 2.38% 24
 2.38% 25
 2.38% 26
 2.38% 27
 2.38% 28
 2.38% 29
 2.38% 30
 2.38% 31
 2.38% 32
 2.38% 33
 2.38% 34
 2.38% 35
 2.38% 36
 2.38% 37
 2.38% 38
 2.38% 39
 2.38% 40
 2.38% 41
 2.38% 42

わかったこと

Sizeって大体MAX42になるよう調整されてる?

sunakansunakan

ありがとうございます

rebar3 proper --numtests 100 --max_size 10000 とすると

 1.00% 1
 1.00% 102
 1.00% 203
 1.00% 304
...
 1.00% 9495
 1.00% 9596
 1.00% 9697
 1.00% 9798
 1.00% 9899
 1.00% 10000

という結果が得られました。
--numtests N回でMaxSizeに行くよう調整されていそうですね

コードでもそれっぽい箇所を確認しました 👍

https://github.com/proper-testing/proper/blob/a5ae5669f01143b0828fc21667d4f5e344aa760b/src/proper.erl#L647-L695

mohiramohira

2024/08/01(木) 20:00-22:30

今日の目標

  • Zennを書く。久々に。

今日のひとこと: 確率的に考えろ!

確率の概念を持ち込むと、いいぞ!!!

実績

  • 4章〜4.3の終わりまで。

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1268523875268821085

次はどこから?

  • 次回: 4.4から

アレコレ

前回の記憶

  • モデル化、事例テストの汎化、オラクル、、プロパティの発見は技巧というかめちゃ技芸
  • ジェネレーターのお陰で、コードが短くなる。これはかなり実際的な意味がある。
  • 事例テストだと、めちゃくちゃコード(与える入力)が増えて、うわあああってなる。
  • 対称プロパティ 例: reverseを2回適用したら、もとに戻るとか
  • 不変なものに着目しよう。例: sortしても、要素数は不変だとか
  • 事例テストも大事やぞ! EBTとPBTは補完の関係になるはず

ジェネレーター自体の検証を人間がしないといけないのだ

collect と aggregate で、ジェネレーターが作る値をちゃんと解析しよう! メトリクスみような!

ジェネレーターを検証するのは、人間
ジェネレーターはジェネレーターを検証しないのだ
あと、統計が大事
「人間み」が大事

aggregate()で度数分布表を作る感じ

oneofジェネレーター

// 1/2でKeyがダブらせることができる

oneof([ 1だけ生成数ジェネレーター, 整数全体から1つ出すジェネレーター()]) 

// 誤解っぽいもの
// こうしちゃうと、重複は起こしやすいが、ランダムな値の範囲がめちゃ狭まって悲しい
1〜100までの整数出すジェネレーター()

「サイズ」とリサイズジェネレーター

Burn in です

そもそも、デフォルトのジェネレータは、徐々に値を複雑にするように内部的に制御している。

イメージ: 序盤は、0とか-1とか4みたいな値。で、試行回数を増やすと、498324902とか3848390483920489230489023185みたいな複雑とかビッグな値になる。

言い換えると、めちゃくちゃBIGな値でしか、バグの可能性がないようなコードをテストしたいのであれば、あったまる時間が必要(最初の方で生成されるデータは、単純だったり小さい値だから)


でも、無駄じゃん! 最初の方、無駄じゃん!
最初からあっためといてよ、という話

異なる要素の相対関係を考えようね

サイズの調整をしたり、nameはbioより短いとか。どっちも string() を使うとしてもさ。

リサイズ。「サイズ」を調整する! 複雑さとかデカさをいじろう。
徐々に複雑になるように内部で調整されていたのだ!

異なる要素の相対関係、大事だぞ! そして、それは、たとえば、サイズを調整することでうまく制御できるのじゃ

フィルター(?SUCHTHATマクロ)は、値を作ったあとで除外するので、無駄試行が増えることに注意な

フィルターを使うときはいつも、制約を変 換に置き換えられないか考えてみてください。確率的に考えるのです。

あとは、そもそも、デフォルトのジェネレータが吐き出す値の(探索)空間は、バカでかいぞ〜!

あくまで、フィルターなので、生成してから除外する、という挙動。つまり、試行回数のコストが効いてくる。

ので、なるべく「変換」が使えるかを考えましょう(?LETマクロ)

?LET マクロ で「変換」

責務をしっかりわける。プロパティ内で、ジェネレーターのための変換をしちゃうと、つらいぜ!?

「タプルを要素に持つキュー」とか、そのままだとヤバいぜ?

ジェネレーターにできれば、組み合わせもできるし、本来確かめたいプロパティがめっちゃ見通し良くなる

frequency()を使ったコードを見ると、そのジェネレーターを実装した人が考えていることが結構、はっきり現れるのがいいな〜って思ったよ

実装者が、英文っぽい文章を想定していることがわかる。ほんで、実はアラビア文字を使うようなサービスなら、不足してるがな! とかを、コードをベースとして議論できるやん?

ほとんど◯◯な値"を、短いコードで生成できるのめっちゃヒントやん!

しかも、そういう値を手作りするのはめちゃくちゃめんどくさい。しかもしかも、簡単というわけでもない

例: ほとんどソート済みのリスト
例: おおよそCSVの形式を満たしているテキスト
例: だいたいあってそうな値

EBTの世界ではあんまり考える機会がないような「確率」の話が入ってくるのがいい

「確率」の概念を持ち込むことで、かなり戦える。ピンポイントの秘孔をつけなくても、「たぶんこのへんのどこかにはあるだろうアプローチ」が、凡人でもちょっといけそう
あとは、「データ分析の前処理のつらみ」を炙りだすときにも、「確率」で戦える感じがある。

「たまに紛れ込んでる変な値」

mohiramohira

2024/08/15(木) 20:00-22:30

今日の目標

  • 4章終わらせるぞ!

今日のひとこと: PBTとEBTは両輪

EBTで、確実な仕様を少数のExampleで表現する。
PBTで、未知のバグを既知のバグにする役割。既知のバグにしたら、EBTで確実に仕留める

実績

  • 4.4〜4章おわりまで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1273598680657825864

次はどこから?

  • 次回: 5章はじめから

アレコレ

前回の記憶

  • 前回のZennをみてね

?SUCHTHATは数学由来の言葉 such that; フィルタリングのイメージ

「such that」とは、「ある条件が成り立つように」または「その条件を満たすように」という意味を示す英語表現である。 おもに数学的・論理学的な文脈で使われるが、一般的な文脈で用いられることもある。 たとえば、「「A such that B」」の場合、AがBの条件下で成り立つことを示す。

シンボリックコールを使うと、「失敗したとき」の「データ生成過程」がわかる 👉 デバッグしやす〜い!

  • logを仕込むとは違う。logだと、成功したケースもlogが出るのでうるさい
  • 細かいところはPropErが演ってくれるし、コードもスッキリ
  • デバッグのためだけのコードが不要になるのでえらい

シンボリックコールは「コードをデータとして運べる」

「テスト実行するときまで、評価しない」ということができる!
→ テストを実行するときに履歴を記録できちゃうわけよ

逆に、dict:store(integer(), integer(), Dict)にしちゃうと、その時点で評価されちゃって、消えちゃうわけ。履歴は。

再帰を使ったジェネレーター; 繰り返し数を固定するといい感じ

いい感じ〜!

コード写経してないし演習問題も飛ばしているけど、いい感じだと思う

いい感じ〜!

mohiramohira

2024/08/29(木) 20:00-22:30

今日の目標

  • 特になし! で、進めるぞ!

今日のひとこと: 漂流するPBT、EBTは錨

  • プロパティベーステストが最適なのはいつか?
  • EBTを使うべきときはいつか?

実績

  • 5章〜5.3おわりまで(CSVのパーサのやつ)

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1278673382858166416

次はどこから?

  • 次回: p.111 5.4 レコードのフィルタリング から

アレコレ

前回の記憶

  • 前回のZennをみてね

対称プロパティの威力

encodeして、decodeしたら元通り。ただ、それだけをプロパティで表現すればよいのじゃ。

prop_roundtrip() ->
	?FORALL(Maps, csv_source(),
			Maps =:= bday_csv:decode(bday_csv:encode(Maps))).

CSVパーサーを実装する過程で筆者もPBTの威力を体感してるの良い

上記のコードの開発にあたっては、何度もテストを実行し、プロパティを満たすべく反復的に実装を改善しました。

こんなCSV思いつかないよ! 実際、作者がそうだった! のを、PBTによって発見してるの、良いね!

  • 例: 1列しかないCSV(CSVの使用を満たしてはいる!)

君は、対象プロパティだけを信じることができるか!? 不安だったら書けばいいじゃない。テストを。

プロパティベーステスト ビギナーあるある

新しく書くテストのほぼすべてが大した意味もなくプロパティになることがよくあります。
この章では、プロパティベーステストが適する事例と、それほど適さない事例をいくつか取り上げます。

「プロパティを緩める」もアリ得るぞ! ==「ジェネレーターが生成する値空間を小さくする」

PBTの絶対カンマを入れようぜ作戦!
1列の値は絶対に生まれない → 制限がきつい → 探索空間が小さくなる = わざと見過ごされる空間がある。 => 「ゆるむ」

CSVのパースの話のおもしろいところ(p.110あたりまで)の議論はかなり興味深いからマジで!

ここまで

  • 2つの対応できないパターンを発見した!

  • a) 1列のCSV)パターン(\r\n\r\nなどは、ジェネレーターで生成させないようにした(RFCの曖昧さに起因)

    • その代わりEBTで、1列CSVの対応を表現した
    %% @doc 1 列の CSV ファイルは、RFC 4180 の末尾の CRLF のために本質的に曖昧であり
    %% このバグは予想されるものである 
    one_column_bug_test() ->
    	?assertEqual("\r\n\r\n", bday_csv:encode([#{""=>""},#{""=>""}])),
    	?assertEqual([#{"" => ""}], bday_csv:decode("\r\n\r\n")).
    
  • b) フィールド名の重複はないやろ前提からくる罠問題(Mapを使っているのでね)

    • 実利的にええやろって判断した。
    • かつ、「ヘッダー重複するCSVまで対応考えなくてええやろ!」ってことにした
    • EBTで、コメントと主に、現在のパーサーでは、フィールド名が重複するCSVは、厳格にパースはできません! と明示した
    • ただ、フィールド名が重複する値を csv_srouce()ジェネレーターが、生み出す可能性は残っている
    • ので、"稀に"(確率的に)テストが失敗することがある
    • そんときどうするの?
  • Q. bのケースは実際どうするの?

    • そもそも厳格なパーサーにする?
    • ジェネレーターがフィールド名が重複する値を生み出さないようにする?
    • shrinkされたときに気付けるも? documentに書いとく?
    • Mapじゃないデータ構造を利用する。Key重複許すデータ構造や、リストにして検証を頑張る。そういうのもある← けど、それはPayしなくない? だるくない?


ここの議論めっちゃおもろくない?

%% @doc RFC から引用した反例。マップには重複キーがないので、現在の実装ではうまくいかない
dupe_keys_unsupported_test() ->
	CSV = "field_name,field_name,field_name\r\n"
		  "aaa,bbb,ccc\r\n"
		  "zzz,yyy,xxx\r\n", 
  [Map1,Map2] = bday_csv:decode(CSV), 
  ?assertEqual(1, length(maps:keys(Map1))), 
  ?assertEqual(1, length(maps:keys(Map2))), 
  ?assertMatch(#{"field_name" := _}, Map1), 
  ?assertMatch(#{"field_name" := _}, Map2).

EBTの威力を明確に言語化する!

ユニットテストのもう 1 つの利点はリグレッション、つまり、明示的に残しておく必要があるバグや頻繁に検証される特定の厄介なバグの保存に役立つことです

これを改めて言語化しているのいいですね!

「テストは(とりあえず)書くべきもの」みたいな言説より、遥かに価値のある表現ですな!

「明示的にバグを保存する」って表現も良いな。

プロパティが"キレイすぎる"と厄介; 一般的っぽくて、前提を見失うかも?

一方でプロパティは、あまりにも一般化された形に書き下すことが可能なので、基本的な点を見 逃してしまいかねません。

mohiramohira

2024/09/12(木) 20:20-22:00

今日の目標

  • 5.4 レコードのフィルタリングを読み切る

今日のひとこと: PBTじゃなくても、悪くないですね!

  • 中島誠之助のようだ
  • 取りうる状態や感がられる組み合わせが有限かつ、コンピュータで十分計算可能ならば、総当たりでええやんけ!
  • PBT連打じゃないようにしような、という気持ち

実績

  • 5.4 レコードのフィルタリング

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1283748790544891995

次はどこから?

次回: p.121 5.5 従業員モジュール から

アレコレ

前回の記憶

  • Zennを読んでね。良いこと書いてるよ。

5.4の目標

この節では、問題空間が大きい場合でも、プロパティよりも事例ベースのテストの ほうが「うまく」探索できる場合があることを示します。

これまでの気持ちは

  • 問題空間が大きい → PBTええやん!
  • 問題空間が小さい(≒特定できる?) => EBTやん!

けど、5.4では、

問題空間が、大きいのに、EBTのほうが適している(PBTよりもうまくいく)ことがある

これを知りましょうって話。

ここでは、従業員名簿のマップを日付でフィルターする例により、プロパティがテス ト手法として最適でない場合でも「事例を思いつく」ための源泉としてやっぱり有用 なことを示します。

PBTがあんまり適さない(EBTを使おう)な話をするんだけど、

でも!

やっぱり、PBTはPBTで有用だという話もするそうです。

逆パターンはよくあるよね。

プロパティが思いつかないときは、EBTで具体例をやって、汎化するやつ。

これの逆パターン。

ところで、5章の例にある、CSVの従業員名簿をもとに誕生日メールを送信するシステム」ってめっちゃ良い題材。自然とイメージできるシステムでありがなら、CSVの厄介さ、日付(閏年まわり)の厄介さ、がちゃんとある

さすがだわ。

テストをreasonableに「緩める」

mohiramohira

2024/09/26(木) 20:10-22:10

今日の目標

  • 5章読み切るぞ!

今日のひとこと:「みなさんはもうかなりいい線にまで到達しています」

  • 前回の「悪くないですね!」につづき、よいフレーズがあって好き
  • 「あまり意味がないメトリクスですが気分はいいですね!」も、よい
  • 「自慢できる成果です」も、よい

実績

  • p.121 5.5 従業員モジュール から p.136 5章おわり まで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1288818723750150176

次はどこから?

次回: p.137 6章 プロパティ駆動開発

アレコレ

前回の記憶

  • Zennを読んでね。良いこと書いてるよ。

PBTという意味では新しいことはやってない節だった

今日作ったプロパティ

  • 先頭の空白除去できますか?
  • yyyy/mm/dd -> Erlangの日付の方 {Year, Month, Day} にできてますか?
  • 一連の流れができてますか? (from_csvとか、アクセサとか) ← 結果的に、カバレッジ100%だった!

というわけで、

  • そんなにプロパティベーステストの新しい話はなかった感じ
  • つまったのは、「Opeque型だのhandle()だの、設計を良くするためのやり方の話」と、「Erlangの文法まわり」

fetchは中身を取り出す人(なぜなら、opaqueにしているから、外部からアクセスできないから)

煩わしい業務ルールだらけの連携作業をやっていくことになります。それら の検証にプロパティがどう活用できるかを見ていきましょう。

👉️ ここはあんまりピンときてないかも?
(いままでと同じという感じ。問題空間をうまくとらえていれば、テストデータ生成が短くて楽でね、ってのは改めておもったところはある)

「あまり意味がないメトリクスですが気分はいいですね!」

これおもろいなww

躍起にならず、無理もせず、きづいたら、カバレッジ100%

これは、たしかに、気分がいいのはそう!

Erlang文法:パターンマッチのメガネがほしい

last_name(#{"last_name" := Name}) -> Name.

lastn_nameフィールドがあれば、そのValueを返す(Nameという名前で束縛している)

Erlang文法: Map

#{ } は 空のmap

https://erlang.org/documentation/doc-6.0/doc/reference_manual/maps.html#:~:text=An empty map is,%23{}

おさらい: ?LETマクロは、値生成したあと、さらに変換をかませるやつ。

この2つのジェネレーターがわかりやすい

even() -> ?LET(N, integer(), N*2). 
uneven() -> ?LET(N, integer(), N*2 + 1).

実際にDBからとってくる仕様になったらコードがどう変わるかは気になる

わかる。

付録についてたらはっぴ〜〜〜

mohiramohira

2024/10/10(木) 20:30-22:00

今日の目標

  • とくになし! 進むぞ!

今日のひとこと:「勉強会のリマインド投稿、意味あるぞ!」

  • 明日です! とか 今日です! とか

実績

  • p.137 6章 プロパティ駆動開発 から p.144 6.3のリード文まで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1293900877706563605

次はどこから?

次回: p.145の「6.3.1 ジェネレーターの方針を考える」から

アレコレ

未解決問題: 「ポジティブテスト」と「ネガティブテスト」って何?

正常系と異常系の話っぽい? 6.5に記述があるので期待。

あるべき姿を明確にすることは、難しい〜!

プログラミングも難しいですが、プログラムのあるべき動作を明確にすることは、それに輪をかけて難しいものです。

これは異論ないでしょ!

きちんと定義された仕様があれば最高ですが、現実の仕様は曖昧なものです。その 状態から始めてみましょう。

ここが5章との違い。5章はかなり仕様がはっきりしていた記憶がある(CSVの仕様とかはさておき)

EBTと違って、PBTではジェネレーターを検証するのを忘れるなっ!

プロパティで実行する百程度のテストが実際に異なることを確かめるために必要です

自作のジェネレーターが、いいかんじで、そこそこ広い範囲で、値作ってくれているかチェック。

もし、すごく小さい値ばっかりとか、アイテム名のバリエーションが少ないと、捉えている問題空間の小ささが問題になるよね? 的な!

自作自演になってもいけないぞ!
(今回はOK! ただの金額数え上げ vs 商品価格リストとマッピングして合計するやつ)

ジェネレータ便利〜〜! 強い力には大きな責任がともなうのだ

少ないコードで広範囲を捉えられるのは嬉しいな。

当然、ジェネレーターをちゃんと検証する責任があるけどね!

続きが気になる...!!

6.3.1 ジェネレーターの方針を考える
いったい、どうやって...??

勉強会はじめてだいたい9ヶ月

  • 2025/01/25からこの勉強会やってる(9ヶ月くらい)
  • 現在16回おわり。
mohiramohira

2024/12/18(木) 20:00-22:30

今日の目標

  • とくになし! 進むぞ!

今日のひとこと: 「このネガティブプロパティにはがっかりです」

  • ひさびさ!

実績

  • p.144 6.3 から 6章のおわりまで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1318895418024919051

次はどこから?

次回: 7章 収縮 から

アレコレ

ハッピーパスの世界とネガティブテストの世界

これ までのプロパティでは、「すべてが正しく動作し、良好な結果が得られる」ことを検 証するための事例をテストしていました。こうしたテストは「ハッピーパステスト」 や「ポジティブテスト」と呼ばれます。

たしかに、これまでは、信じて良い値しか作ってなかった!

たとえば、

  • 商品リストにない商品
  • 商品リストが空である

とか!
クレイジーな値がなかった!!!!

このネガティブプロパティにはがっかりです。

ゆるゆっるの範囲でテストしたのだから、もっと失敗のパターンのバラエティをたくさん検出してくれることを期待していたのに...

結局、90%、未知の商品の例だけですか。

君にはがっかりだよ...

帰ってくれないか!

育ったジェネレーターをベースに、ちょっとだけそのジェネレーターの制約をちょっといじるだけで、結構な発見があるの、おもろい

雑なノリでジェネレーターをいじるだけで、発見がえられるのはつよい
事例ベーステストだと、考えないといけなくて、しかもコードにするのも手間

重複データをつくるための鳩の巣原理なるほど...!!!

かしこい〜

mohiramohira

2025/01/15(木) 20:00-22:00

今日の目標

  • とくになし!

今日のひとこと: 「焼きなまし! と 収縮の仕組み」

  • あけおめ!

実績

  • 7章 収縮 から 8.1 まで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1329043592332709950

次はどこから?

次回: p.192 次回 8.2 実践 標的型プロパティ

アレコレ

収縮の嬉しさ

  • (1) 複雑でわかりにくい事例が見つかるこ
  • (2) 失敗へと至る入力(ジェネレーターが生成した データ)を単純な反例へと落とし込めることにあります。 👈️ こっちのが重要という主張!

収縮の仕組み

失敗したまま「ゼロ点」に向かってデータを変えていく。リストからのリストへ、文字列ならAへ、みたいな感じ。ありがとうフレームワーク。

標的型プロパティの実力はまだわからず

すごそうではある。次回がより楽しみ

mohiramohira

2025-01-29(木) 20:00-22:00

今日の目標

  • とくになし!

今日のひとこと: 標的型プロパティ、独創的、感動!

すげえや! 8.3まじでおもろいよ

実績

  • 8.2から8章おわりまで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1329043592332709950

次はどこから?

次回: p.205 第 III 部 ステートフルプロパティ

アレコレ

標的型プロパティの"標的"感

あやしいところをねらい打てるぞ

?MAXIMIZEのほうが、ゆるすごい雰囲気。すごく少ないコストで、いい感じに事例をつくってくれるのマジですごい。

あと、近傍関数はテクニカルすぎて、テクい。まずはデフォルトからいきましょう。

洗練されているけど、多様性がないデータ

なるほどね〜

関数の実行時間を最大化する事例を探せるパワー

関数の実行に時間がより長くかかる事例を探したいので、実行時間を最大化することを考えます。
手書きの独自ジェネレーターを調整してこれを簡単に実現する方法は想像もつきません。

すげえや!

余談: 同値類、商集合、剰余類

1/2 と 2/4 と 3/6 が全部同じな理由。

mohiramohira

2025-02-12(木) 20:00-22:45

今日の目標

  • とくになし!

今日のひとこと: ついきにきたな、ステートフル

実にうまい話です。モデル化できるものはすべて、ここで見たようなプロパティを 使ってテストできるのです。

実績

  • 9章はじめから9.4おわりまで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1339189977304797204

次はどこから?

次回: p.231 9.5 並列実行をテストする

次回、ステートフルプロパティテストで並行性、やっちゃうぞ〜〜〜〜!!!

アレコレ

ステートフルプロパティの基本がちょっとわかったぞ

疲れたのでメモはここまで。

でも、いい感じだよたぶん。

「モデル」の意味

状態とコマンドを含んだものと読んでいたんだけど、どうも「状態」だけを指すっぽい?

mohiramohira

2025-02-26(木) 20:30-22:15

今日の目標

  • とくになし!

今日のひとこと: ひゅ〜〜〜

ひゅ〜るり〜

実績

  • p.231 9.5 並列実行をテストする から 9章終わりまで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1344272396911251456

次はどこから?

次回: 10章はじめから

アレコレ

  • あんまよくわかってないところがある!
  • メモするのとイベント作成を忘れてたので雑! まあそういう日もある!
{[
    [
        {set,{var,2},{call,cache,find, [-4]}},
        {set,{var,3},{call,cache,cache,[7,22]}},
        {set,{var,4},{call,cache,cache,[2,3]}}
    ],
    ------- 以下は、多分並列に実行しているところ(`[`が1個多いから) ------
    [
        [
            {set,{var,5}, {call,cache,cache,[9,15]}},
            {set,{var,6}, {call,cache, find,[2]}},
            {set,{var,7}, {call,cache,cache,[2,14]}}, 👈️ 2を追加している
            {set,{var,8}, {call,cache,cache,[-1,2]}},
            {set,{var,12},{call,cache, find,[4]}}
        ],
        [
            {set,{var,11},{call,cache,flush,[]}},
            {set,{var,14},{call,cache,cache,[2,-27]}} 👈️ ここでも2を追加している
        ]
    ]    
mohiramohira

2025-03-26(木) 20:30-22:15

今日の目標

  • 参加する

今日のひとこと: つかれたときこそ勉強会!

つかれた!

実績

  • 10.3 広範囲のステートフルテスト〜10.4owariまで

Discord開始リンク

https://discord.com/channels/508093437187457045/1190292563907838053/1354418619483689121

次はどこから?

次回: p.270 10.5 テストを改善する

アレコレ

  • PBTでプロパティを考えることで、システムを考えることがうまくなりそう
  • コマンドを2つのグループ(いつでも成功 | 状態依存)に区別するアプローチよかった。MECEって大事かつ、どこで協会を作るかは大事
  • 実家の母からの手紙めいた教訓
  • つかれた(ている)
mohiramohira

2025/04/09(木) 20:30

今日の目標

  • 元気よく!

今日のひとこと

ステートフルプロパティ、ちょっとわかってきた!

実績

  • 10.5 と 10.6

Discord開始リンク

次はどこから?

次回 p.283 10.7 並列テスト

いろいろ

  • FWが、ステートフルプロパティの語彙が、浸透してきている...!!! => どこをみればよいかがだいぶ分かってきた!

バグ自体を時間で評価する

「バグの発見タイミング」と「そのバグの原因となるリリースのタイミング」を比較して、その感覚が長いほどよい! 短いなら、「もっと、頑張れたんじゃない?」「早く直すのもいいけど、もっとやれたんじゃないか?

mohiramohira

2025/04/23(木) 20:30-22:00

今日の目標

  • いつもどおり

今日のひとこと

俺達は一体何のシステムを作っているんだぜ?

実績

  • 10.7 と 11.1〜11.3

Discord開始リンク

次はどこから?

次回 p.293 11.4 サーキットブレーカーのテスト

いろいろ

  • 「有限状態機械プロパティ」
  • 何のシステムを作っているか教えてくれー!
  • もうちょいで終わりそう
ログインするとコメントできます