Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python hypothesis: Ensure that input lists have same length

I'm using hypothesis to test a function that takes two lists of equal length as input.

import hypothesis.strategies as st
from hypothesis import assume, given


@given(st.lists(ints, min_size=1),
       st.lists(ints, min_size=1),
       )
def test_my_func(x, y):
    assume(len(x) == len(y))

    # Assertions

This gives me the error message:

FailedHealthCheck: It looks like your strategy is filtering out a lot of data. Health check found 50 filtered examples but only 4 good ones.

The assumption that len(x) == len(y) is filtering out too many inputs. So I would like to generate a random positive number and use that as the length of both x and y. Is there a way this can be done?

like image 573
Vermillion Avatar asked Jul 30 '18 15:07

Vermillion


3 Answers

I found an answer using the @composite decorator.

import hypothesis.strategies as st
from hypothesis import given

@st.composite
def same_len_lists(draw):

    n = draw(st.integers(min_value=1, max_value=50))
    fixed_length_list = st.lists(st.integers(), min_size=n, max_size=n)

    return (draw(fixed_length_list), draw(fixed_length_list))


@given(same_len_lists())
def test_my_func(lists):

    x, y = lists

    # Assertions
like image 163
Vermillion Avatar answered Nov 20 '22 04:11

Vermillion


The other solutions give nice reusable strategies. Here's a short low-tech solution, perhaps better suited to one-off use since you need to do one line of processing in the test function. We use zip to tranpose a list of pairs (2-element tuples); conceptually we're turning a n x 2 matrix into a 2 x n matrix.

import hypothesis.strategies as st
from hypothesis import given

pair_lists = st.lists(st.tuples(st.integers(), st.integers()), min_size=1)

@given(pair_lists)
def test_my_func(L):
    x, y = map(list, zip(*L))

Warning: It is crucial to have min_size=1 because zip will give nothing if the list is empty.

like image 45
Giles Gardam Avatar answered Nov 20 '22 03:11

Giles Gardam


You can use flatmap to generate data that depends on other generated data.

import hypothesis.strategies as st
from hypothesis import assume, given
from hypothesis.strategies import integers as ints

same_len_lists = ints(min_value=1, max_value=100).flatmap(lambda n: st.lists(st.lists(ints(), min_size=n, max_size=n), min_size=2, max_size=2))

@given(same_len_lists)
def test_my_func(lists):
    x, y = lists
    assume(len(x) == len(y))

It's a little clumsy, and I'm not very happy about having to unpack the lists inside the test body.

like image 6
Patrick Haugh Avatar answered Nov 20 '22 04:11

Patrick Haugh