Passing fixtures to tests in pytest

George Shuklin
Python Pandemonium
Published in
2 min readDec 9, 2016

I’m working on project which uses pytest to run testinfra tests against cloud instances. Those tests should have information about servers they ought to test (IP address and credentials is a bare minimum).

After some thoughts I decided to create fixture(s) with all relevant information. The question I stumble was ‘how to pass it to pytest’? One of possible solutions is provided below.

Calling tests

To call test from python code we can just invoke pytest.main() with all relevant arguments:

import pytest
pytest.main([path_to_test, '-v'])

Creating fixture

To create fixture we can use decorator @pytest.fixture:

@pytest.fixture
def info(request):
return "Information!"

Passing plugin to pytest

To pass something to pytest-executed tests we can use plugin mechanism of pytest. (For not let says that ‘myplugin’ contains actual plugin, I’ll show later how to create it).

pytest.main([path_to_test, '-v'], plugins=[myplugin])

(Please not that ‘plugins’ takes a list of plugins, not plugin itself)

Creating plugin

There are two ways to create plugin:

  1. Use stand-alone module
  2. Create class or other ‘dotted’ object (object with members accessible by dot: plugin.something).

Module way

Use a separate file to contains plugin code:

myplugin.py:

import pytest@pytest.fixture
def info(request):
return "Information"

actual call of pytest:

import myplugin
pytest.main(['mytest.py', '-v'], plugins=[myplugin])

Fixture usage (mytest.py):

def test_foo(info):
assert info() == 'Information"

Class way

This way I found more preferable, because I have higher degree of freedom during class initialization. I can stuff all information I want inside class in very natural way.

Plugin and fixture constuction and test call:

import pytestclass MyPlugin(object):
def __init__(self, data):
self.data = data
@pytest.fixture
def info(self, request):
return self.data
myplugin = MyPlugin('information')pytest.main("mytest.py", plugins=[myplugin])

Test is exactly the same as before (mytest.py):

def test_foo(info):
assert info() == ‘Information”

More than one fixture

It is very easy to put any number of fixtures into a class:

import pytestclass MyPlugin(object):
def __init__(self, data):
self.data = data
@pytest.fixture
def info(self, request):
return self.data
@pytest.fixture
def length(self, request):
return len(self.data)
myplugin = MyPlugin('information')pytest.main("mytest.py", plugins=[myplugin])

A bit upgraded version of test accepting two fixtures (mytest.py):

def test_foo(info, length):
assert len(info()) == length()

Conclusion

Combination of pytest and testinfra with own plugins allows elegant and semantical way to integrate application-specific information into build-in test framework.

--

--

George Shuklin
Python Pandemonium

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