yielding closure from a fixture in pytest

George Shuklin
OpsOps
Published in
1 min readJan 20, 2021

There is a pattern I occasionally see. There is a fixture, which need to do something in the middle of the test. It’s a fixture because it has dependency on other fixtures (in my case host magical fixture of testinfra), and it need to do cleanup after test was executed.

One undignified way is to return a function:

@pytest.fixture()
def myfixture():
def retfun():
doing_something()
return retfun

In this case test can use myfixture as a function:

def test_foo(myfixture):
myfixture()

There is no cleanup stage here, and nested ‘def’ is always kinda hard to read.

I’ve just make it a bit more functional.

def put_data(host):
yield lambda: (
host.check_output('/usr/bin/truncate testblob --size 1337')
)
host.check_output('rm testblob')

The call from the test is the same:

def test_foo(put_data):
do_something()
put_data()
assert result()

The key advantages:

  1. There is no excessive ‘def’ declaration. Labmda is almost the same as the function, but it clearly shows that it’s a ‘run once’ code.
  2. yield lambda make it looks closer to a return of the closure
  3. yield instead of return allows to do cleanup (even after test failed!)
  4. finally, those brackets around lambda body allows to put lambda body on a separate line, making it easier to read.

I clearly understand that there is no difference between yield func and yield lambda, but combination of yield and lambda in a single line (instead of separate def) making it more concise, in my opinion.

--

--

George Shuklin
OpsOps

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.