2.1 KiB
| tags | ||
|---|---|---|
|
Fixtures are a way to provide data or state to your tests. Typically, although
not exclusively, for processes that you want to reuse accross different test_
functions. They are defined at the "arrange" stage of testing.
Example: testing class methods under test
class Person:
def __init__(self, name):
self.name = name
def print_name(self):
return f"Your name is {self.name}"
To create a fixture of this class:
@pytest.fixture
def my_person():
person = new Person('Thomas')
return person.name()
test_print_name(my_person):
assert(my_person) == 'Thomas'
Note we define our fixture and then inject it into our test function. Functions request fixtures they require by declaring them as arguments.
Alternatively you could just return the class from the fixture and test the methods individually within the test function.
Integrating with @patch to provide mock fixtures
Say we wanted to mock reading a JSON file.
We could create a fixture:
@pytest fixture
def raw_json():
return json.dumps({"key": "value"})
And pass it in to the mock of the global open method:
from unittest.mock import mock_open, patch
def test_json_parsing(raw_json):
with patch("builtins.open", mock_open(read_data=raw_json))
# Then assert etc
This fixture could then be re-used to test any other method that relies on incoming JSON from a file.
Example: setup and teardown - before each and after each
Fixtures can be leveraged to setup 'before each...' and 'after each...' logic:
@pytest.fixture(scope="function")
def setup():
os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"
yield
del os.environ["POCKET_LAMBDA_ENDPOINT"]
Here yield is a placeholder for the tests that run in between the setup and
teardown.
We use scope="function" to signal that this will run before/after any tests
that inject the fixture.
Then to specify the units that will run as the yield we just inject setup:
def some_function(setup_function):
pass