[Python] How to test a form with unique fields using Hypothesis?

Discussão em 'Python' iniciado por Stack, Junho 21, 2025.

  1. Stack

    Stack Membro Participativo

    I have a model with an unique constrain linking three fields:

    class Bookshelf(models.Model):
    house = models.CharField(max_length=2, choices=HOUSE_CHOICES, default='Ar')
    room = models.CharField(max_length=20, choices=ROOM_CHOICES)
    name = models.CharField(max_length=200)
    compartments = models.IntegerField(choices=[(x, str(x)) for x in range(1, 9)])
    shelves = models.IntegerField(choices=[(x, str(x)) for x in range(1, 9)])

    class Meta:
    constraints = [
    models.UniqueConstraint(fields=['house', 'room', 'name'], name='unique_library')
    ]

    class Book(models.Model):
    title = models.CharField(max_length=200)
    bookshelf = models.ForeignKey(Bookshelf, on_delete=models.PROTECT, blank=True, null=True)
    ...


    and a modelform to test using hypothesis:

    class SearchForm(forms.ModelForm):
    class Meta:
    model = Book
    fields = ['title', 'author_name', 'main_topic', 'subtopic', 'bookshelf', 'vote', 'support']

    def __init__(self, *args, **kwargs):
    super(SearchForm, self).__init__(*args, **kwargs)
    ...


    My tests often fail with flaky behavior (when I use assume() or exist()) or an IntegrityError. It seems that Hypothesis creates a Bookshelf instance and leaves it in the database between test runs or within the same run. So, when it tries to create a Bookshelf with the same (house, room, name) combination again, it violates the unique constraint. Here same things I try (a little mixed up), I can be more specific about what gives what error:

    from hypothesis.strategies import data as hypothesis_data

    @composite
    def search_form_data(draw):
    house = draw(st.text(min_size=1, max_size=10))
    room = draw(st.text(min_size=1, max_size=10))
    name = draw(st.text(min_size=1, max_size=10))

    bookshelf = Bookshelf.objects.create(
    house=house,
    room=room,
    name=name,
    compartments=2,
    shelves=5
    )
    # assume(not Bookshelf.objects.filter(house=house, room=room, name=name).exists())
    # if not Bookshelf.objects.filter(house=house, room=room, name=name).exists():

    return {
    'title': draw(st.text(min_size=0, max_size=30)),
    'author_name': draw(st.text(min_size=0, max_size=30)),
    'bookshelf': bookshelf.id,
    }


    @given(data=search_form_data())
    def test_search_form_accepts_random_valid_input(data):
    form = SearchForm(data)
    try:
    form.is_valid()
    except Exception as e:
    pytest.fail(f"Unexpected exception: {e}")


    @given(
    unique_houses=st.sets(st.text(min_size=1, max_size=2), min_size=5, max_size=5),
    data=hypothesis_data()
    )
    def test_search_form_accepts_random_valid_input(unique_houses, data):
    for house in unique_houses:
    form_data = data.draw(search_form_data(house), label=f"form_data_for_{house}")
    form = SearchForm(form_data)

    try:
    form.is_valid()
    except Exception as e:
    pytest.fail(f"Unexpected exception: {e}")


    @composite
    def unique_bookshelf_triplet(draw):
    triplet_strategy = st.tuples(
    st.text(min_size=1, max_size=3),
    st.text(min_size=1, max_size=10),
    st.text(min_size=1, max_size=10),
    )
    seen = draw(st.shared(st.builds(set), key="triplets"))

    return draw(
    triplet_strategy
    .filter(lambda x: x not in seen)
    .map(lambda x: seen.add(x) or x)
    )

    @given(data=hypothesis_data())
    def test_create_unique_bookshelves(data):
    for _ in range(5):
    house, room, name = data.draw(unique_bookshelf_triplet())

    bookshelf = Bookshelf.objects.create(
    house=house,
    room=room,
    name=name,
    compartments=data.draw(st.integers(min_value=1, max_value=10)),
    shelves=data.draw(st.integers(min_value=1, max_value=10)),
    )

    assert bookshelf.pk is not None


    So if I don't check, hypothesis gives many duplicate (integrityerror), If I check the tests become flaky (too many excluded or inconsistent), I tried to generate unique strategies but the results are flaky, probabily I do something very wrong because it's a common situation so it should be easy

    Thank you for your help

    Continue reading...

Compartilhe esta Página