Skip to content

Instantly share code, notes, and snippets.

@untitaker
Last active November 20, 2023 11:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save untitaker/49a05d4ea9c426b179e9 to your computer and use it in GitHub Desktop.
Save untitaker/49a05d4ea9c426b179e9 to your computer and use it in GitHub Desktop.
# Use this instead of `hypothesis.given`
# Basically does the same thing as the newtest fixture, but with better ergonomics and more terrible hacks
def given(*given_a, **given_kw):
import inspect
from hypothesis import given as _given
from hypothesis.internal.compat import getargspec
from hypothesis.internal.reflection import impersonate, copy_argspec
from _pytest.python import Function
def decorator(f):
f_argspec = getargspec(f)
pytest_args = list(f_argspec.args[len(given_a):])
for k in given_kw:
pytest_args.remove(k)
pytest_argspec = inspect.ArgSpec(
args=pytest_args,
keywords=f_argspec.keywords,
varargs=f_argspec.varargs,
defaults=f_argspec.defaults
)
hyp_args = list(f_argspec.args[:len(given_a) + 1])
for a in tuple(hyp_args):
if a not in given_kw:
hyp_args.remove(a)
hyp_args = ['request'] + hyp_args
@_given(*given_a, **given_kw)
@impersonate(f)
@copy_argspec(f.__name__, inspect.ArgSpec(
args=hyp_args,
keywords=f_argspec.keywords,
varargs=f_argspec.varargs,
defaults=f_argspec.defaults
))
def wrapper(request, *h_a, **h_kw):
@impersonate(f)
@copy_argspec('test_inner', pytest_argspec)
def callobj(*a, **kw):
return f(*h_a, *a, **h_kw, **kw)
parent_test = request.node
item = Function(
name=request.function.__name__ + '[]',
parent=parent_test.parent,
callobj=callobj
)
nextitem = parent_test
item.ihook.pytest_runtest_setup(item=item)
item.ihook.pytest_runtest_call(item=item)
item.ihook.pytest_runtest_teardown(item=item, nextitem=nextitem)
return wrapper
return decorator
import pytest
from _pytest.python import Function
@pytest.fixture(scope='module')
def thing():
return object()
@pytest.fixture
def newtest(request):
parent_test = request.node
def inner(func):
item = Function(
name=request.function.__name__ + '[]',
parent=parent_test.parent,
callobj=func,
)
nextitem = parent_test # prevents pytest from tearing down module fixtures
item.ihook.pytest_runtest_setup(item=item)
item.ihook.pytest_runtest_call(item=item)
item.ihook.pytest_runtest_teardown(item=item, nextitem=nextitem)
return inner
from hypothesis import given
from hypothesis.strategies import text
@given(s=text())
def test_lol(thing, newtest, s):
outer_thing = thing
@newtest
def test_inner(tmpdir, thing):
assert thing is outer_thing
assert not tmpdir.listdir() # this fails without newtest fixture
tmpdir.join('lol').write(repr(s))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment