Vermillion
Vermillion

Reputation: 1308

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?

Upvotes: 15

Views: 2637

Answers (4)

mechnicov
mechnicov

Reputation: 15288

What's about using dictionaries for this purpose?

You can specify generated data

For example if need only positive integers, use min_value=1. If don't need empty list - min_size=1 for dictionary, etc.

from hypothesis import given
from hypothesis.strategies import integers, dictionaries


@given(
    dicts=dictionaries(integers(min_value=1), integers(min_value=1), min_size=1)
)
def test_your_test(dicts):
    x = list(dicts.values()) # or just dicts.values()
    y = list(dicts.keys()) # or just dicts.keys()

    assert len(x) == len(y)

Same way you can generate any lists with same length (list of strings or whatever)

Upvotes: 0

Giles Gardam
Giles Gardam

Reputation: 1402

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.

Upvotes: 1

Vermillion
Vermillion

Reputation: 1308

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

Upvotes: 17

Patrick Haugh
Patrick Haugh

Reputation: 60994

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.

Upvotes: 7

Related Questions