🐞

Hypothesisで組み合わせでストラテジーを作る

2022/06/20に公開

hypothesis.strategies にあるコアなストラテジーはよく使うけれども、それらを組み合わせてストラテジーを作りたいことがよくある。そういう場合にはいくつかの方法がある。

one_of を使う(| を使う)

one_of に渡した複数のストラテジーの直和型のストラテジーが生成できる。たとえば数値か真偽値を返すストラテジーを作りたいという場合には次のようになる。

from hypothesis.strategies import booleans, integers, one_of

int_or_bool = one_of(integers(), booleans())

for _ in range(0, 5):
    print(int_or_bool.example())

これを実行すると次のようになる。

-149
False
47081944555646014
-30273
-3379797192290189948

| で連結してあげても複数のストラテジーの直和型のストラテジーが生成できる。先ほどと同様の例であれば

from hypothesis.strategies import booleans, integers

int_or_bool = integers() | booleas()

for _ in range(0, 5):
    print(int_or_bool.example())

これを実行すると次のようになる

127
0
False
-966895869
3

ここで one_of| で何が違うのかといえば、何も違わず、|one_of のシュガーになっている。ともに hypothesis.strategies.OneOfStorategy を返している。

composite を使う

まだ one_of で済む程度であればいいが、より複雑に組み合わせたい場合は、 @composite を使って、新しいストラテジーを定義したほうが @given にわたす場合に見通しが良くなる。

from hypothesis import given
from hypothesis.strategies import booleans, composite, integers, lists

@composite
def list_of_int_or_bool(draw):
    int_or_bool = one_of(booleans(), integers())
    return draw(lists(int_or_bool))

@given(list_of_int_or_bool)
def test_foo(x):
    return any(isinstance(a, boolean) foor a in x)

この @composite でデコレートされた関数の引数となっている draw は特別な関数で、用途としては関数内で各ストラテジーの example メソッドを呼んだのと同じような意味合いになっている。

Discussion