寄付窓口はこちら

SwiftCheckで始めるProperty-based Testing | try! Swift Tokyo 2019 2-10

関数が満たすべき論理的性質を記述し、ランダムな入力値を生成させてテストを行うProperty-based Testingというテスト手法があります。HaskellのQuickCheckが大元となっていますが、多数の言語に移植されており、SwiftでもSwiftCheckというOSSが開発されています。このトークではProperty-based Testingの考え方と、SwiftCheckでの記述方法を解説をします。

f:id:niwatako:20190322150749j:plain

f:id:niwatako:20190322150813j:plain

ワインが好きですがビールを飲んでいるアイコンです。ここからは日本語で喋ります。

try! Swift前日に英語のワークショップをやったのでせめてここまでは英語で。

f:id:niwatako:20190322150857j:plain

f:id:niwatako:20190322150906j:plain

プライベートではペンギン村コミュニティでサービス開発しています

f:id:niwatako:20190322150916j:plain

f:id:niwatako:20190322150931j:plain

f:id:niwatako:20190322150940j:plain

Property-based testの説明から

f:id:niwatako:20190322151005j:plain

f:id:niwatako:20190322151007j:plain

性質をテストする、ランダムな値でテストする。なんのこと?

XC Testと比較する

f:id:niwatako:20190322151030j:plain

Example based test 明確な入力と期待値を設定

f:id:niwatako:20190322151036j:plain

例えば配列の反転処理。

入力値として与えられた配列を全体反転したものを返す。

f:id:niwatako:20190322151111j:plain

どれだけの入力パターンを試せば良いのか...?

f:id:niwatako:20190322151128j:plain

このぐらい?

f:id:niwatako:20190322151151j:plain

現実には無限に可能性がある

バグがありそうなものを選んでテストしている

f:id:niwatako:20190322151205j:plain

パターンに見落としがあればバグとして検出される

異なる立場を取る

f:id:niwatako:20190322151224j:plain

具体的入力値は考えない

f:id:niwatako:20190322151234j:plain

配列サイズは反転しても変わらない。

f:id:niwatako:20190322151250j:plain

これは関数が持つ性質といえる。

reverseを2回適用すると者に戻るという性質も言える。

f:id:niwatako:20190322151314j:plain

今回は2つの性質に注目しました

f:id:niwatako:20190322151322j:plain

繰り返しますが、これはどんな配列にも共通の性質、関数が見対している性質です。

擬似テストコードで表すと

f:id:niwatako:20190322151347j:plain

このように自分で書くこともできるが、せっかくなのでSwiftCheckを使います。

f:id:niwatako:20190322151433j:plain

XCTestと組み合わせて使える。Swift以外だとScalaCheckが有名だと思います。

f:id:niwatako:20190322151516j:plain

SwiftはHaskellに影響を受けた言語と言われています。

パターンなどを活用できると思えてくると思います。

f:id:niwatako:20190322151545j:plain

f:id:niwatako:20190322151550j:plain

f:id:niwatako:20190322151555j:plain

この引数にランダムな値がくる

f:id:niwatako:20190322151559j:plain

f:id:niwatako:20190322151614j:plain

どのような値が生成されているのか

出力したもの

f:id:niwatako:20190322151634j:plain

毎回ランダム、SwiftCheckによって100のテストがPassしている。

失敗するとこうなる

f:id:niwatako:20190322151654j:plain

からの配列がPassしなかったことが分かる

f:id:niwatako:20190322151708j:plain

f:id:niwatako:20190322151726j:plain

任意の方がランダム生成に対応できる。標準はSwiftCheckによって提供されている

f:id:niwatako:20190322151730j:plain

f:id:niwatako:20190322151759j:plain

座標を表す型でやってみます

f:id:niwatako:20190322151805j:plain

f:id:niwatako:20190322151827j:plain

f:id:niwatako:20190322151839j:plain

2つ目のキーワード

より小さな値にする

f:id:niwatako:20190322151847j:plain

f:id:niwatako:20190322151855j:plain

さっきの登場したやつ

f:id:niwatako:20190322151910j:plain

2つ目のshrink関数

10が失敗したらより小さな値でテストする、それでも失敗したら、、、を繰り返す

ここでテストに成功したとする

f:id:niwatako:20190322151950j:plain

最小の失敗ケース1が報告される。デバッグ調査が容易。

具体的利用ケース

f:id:niwatako:20190322152017j:plain

すべてではないが

f:id:niwatako:20190322152025j:plain

ランダム性のアルゴリズム

f:id:niwatako:20190322152046j:plain

例えば迷路

ExampleBaseなテストでは難しい。

視点を変えると1つの性質がある。

f:id:niwatako:20190322152114j:plain

スタートからゴールにたどり着ける性質がある

f:id:niwatako:20190322152132j:plain

乱数シードを生成して、生成した迷路が常に溶けることを検証する

対称性のあるアルゴリズム

f:id:niwatako:20190322152150j:plain

エンコードしてデコードしたら

もとに戻ることが期待される

f:id:niwatako:20190322152216j:plain

簡単に記述できる

f:id:niwatako:20190322152225j:plain

f:id:niwatako:20190322152241j:plain

最後の例として高速のアルゴリズム対低速アルゴリズム

f:id:niwatako:20190322152255j:plain

一般的に高速アルゴリズムは複雑になりがち

明白なアルゴリズムは低速になりがち

f:id:niwatako:20190322152323j:plain

当たり前だが両者の検証に

f:id:niwatako:20190322152328j:plain

複雑だが高速なアルゴリズムのテストに、明白だが低速なアルゴリズムを利用することができる

f:id:niwatako:20190322152411j:plain

まとめ

f:id:niwatako:20190322152415j:plain

f:id:niwatako:20190322152422j:plain

性質をテスト

テスト値はランダム生成される

SwiftCheckはHaskellのQuickCheckにインスパイアされたもの

Example Testはふようになるか

置き換えるものではない

f:id:niwatako:20190322152502j:plain

特に乱数に頼るのは、言い換えると確立に頼ったものであるため最初は抵抗感がありました。

Example based Testがリファクタリング時の不安を軽減させるし、PropertybasedTestはテストに漏れがないかの不安を軽減させてくれます。

本日の発表は以上です

f:id:niwatako:20190322152557j:plain

ご清聴ありがとうございました。