実践Persimmon.Dried
Arbitrary
Persimmon.Driedでは、引数候補はかなり柔軟に指定することができます。 Arb.int
や Arb.byte
、 Arb.string
などの基本型や、 Arb.list Arb.int
、 Arb.array Arb.float
、 Arb.map Arb.int Arb.string
のようなコレクション型も指定できます。
関数や System.Func
を引数候補にすることもできます:
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
ここに出てくる CoArb
というモジュールですが、「(引数候補である)関数の引数候補」を表すもので、Arbitrary と同じような雰囲気で使えばいいと思います。
また、コレクション型を引数候補にする際、空であったり null であったりしてほしくない場合があると思いますが、それも柔軟に指定可能です:
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
Arbtrary と Gen
Persimmon.Dried を使っていく中で、もうちょっときめ細かく引数候補を生成したくなる場面が出てくるかもしれません。その場合、既存の Arbitrary を少しカスタマイズした Arbitrary を利用することができます。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: |
|
基本的な Arbitrary は Gen
、Shrinker
、PrettyPrinter
の3つの値(ラベル)を持つレコード型です。このうち Gen
: Gen<'T>
が値の生成を担っている箇所なので、ここをうまく調節することで期待する結果を導きやすくなります。
Gen モジュールには、Gen<’T> 型の値を扱う便利な関数がたくさん定義されています。以下に一例を挙げてみます。
oneOf
oneOf
関数は、Gen<'T> 型のシーケンスを受け取り、そのうちのいずれかの Gen<'T> の要素を返します。
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
suchThat
suchThat
関数は、ある Gen<'T> 型の値に対し、指定した条件にあう結果を生成するような Gen<'T> を返します。
1: 2: 3: 4: 5: 6: 7: 8: |
|
listOfLength
listOfLength
関数は、引数に長さと Gen<'T> 型の値を受け取り、指定した長さである 'T 型のリストを生成する Gen<'T list> 型の値を返します。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: |
|
類似の関数に、指定した長さ以下のリストを作る listOfMaxLength
、指定した長さ以上のリストを作る listOfMinLength
などがあります。
他にも有用な関数がありますので、Gen モジュールの中を Intellisense で眺めてみたり、ソースコードを紐解いてみることをオススメします。
様々な性質の書き方
Prop
モジュールには、性質を書く上で便利な関数や演算子が定義されています。
And / Or 演算子
And 演算子 .&.
は、両辺がともに成り立つ場合に全体として性質が成り立ちます。
1: 2: 3: 4: 5: 6: |
|
一方、Or 演算子 .|.
は、両辺のいずれかが成り立つ場合に全体として性質が成り立ちます。
1: 2: 3: 4: 5: 6: |
|
上記2つの性質を実行すると、前者の and operator は失敗しますが、後者の or operator は成功します。
なお、ここで使った |@
および @|
はラベル演算子で、それぞれ p |@ s
、s @| p
のように使い、性質 p にラベル s を付けることができます。
条件付き性質
==>
演算子は、左辺の性質が成り立つ場合にのみ、右辺の性質を確認します。
1: 2: 3: 4: 5: |
|
上記例の場合、入力されたリスト ls の長さが5よりも大きい場合に、i :: ls の長さが6より大きいか確認します。
前述の .&.
、.|.
演算子も同様に、両側に性質を取るような演算子では、右辺の性質は lazy
で包んだ値でなければなりません。これは F# が正格評価を主とする言語であることに伴う制約です。右辺の性質を評価した場合に例外が発生するようなケースでは、演算子に両辺の性質を渡すタイミングで例外が発生し、失敗扱いとなってしまうからです。
1: 2: 3: 4: |
|
分類する
classify
関数は、性質に名前を付けられる点ではラベル演算子のようですが、さらに任意の条件で分類することができます。
1: 2: 3: 4: 5: 6: 7: |
|
これを実行すると、次のように表示されるかもしれません:
1:
|
|
|
ここから、テストに失敗した時の引数の分布が分かります。
Full name: Practice.prms
Full name: Practice.( function as parameter )
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Full name: Practice.( System.Func as parameter )
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
Full name: Practice.( non empty list )
Full name: Microsoft.FSharp.Collections.list<_>
module List
from Microsoft.FSharp.Collections
--------------------
Full name: Practice.( non null array )
Full name: Microsoft.FSharp.Core.array<_>
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Array.length
Full name: Practice.arbLs
Full name: Practice.( list of max length 3 )
Full name: Practice.arbOneOf
Full name: Practice.( get one of prime numbers )
Full name: Practice.arbEven
Full name: Practice.( only even number )
Full name: Practice.arbList3
Full name: Practice.arbList5
Full name: Practice.( list of length )
Full name: Practice.( and operator )
Full name: Practice.( or operator )
Full name: Practice.( conditional property )
Full name: Practice.( right hand side executes... what? )
Full name: Practice.( classifying test case )