• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 8/30/22

Use Fixtures

I know exactly what you’re thinking: here comes another complicated concept that I need to get my head around! But think again! In this chapter, you’ll see that fixtures aren’t complicated to set up and are very useful when creating your tests.

So, what are fixtures then?

The purpose of a fixture is to provide a controlled development environment so that you can configure a set of tests using the same context or the same dataset. This means that the environment created by the fixture will be consistent and identical for each test that calls it.

A simpler way of describing it would be to say that the fixture contains everything a test needs to do its work and all of this is encapsulated into a single function.

You might already be aware of some examples where fixtures are used:

  • Preparing objects

  • Starting or stopping services

  • Initializing a database with a dataset

  • Creating a test client for a web project

  • Configuring mocks

But don’t worry if it’s still not very clear. Let’s look at a specific example.

Let’s say that you created a web project using the Django framework and you set up lots of features that required creating an account and being logged in as the user of the account. Up to now, you’ve had to add lines of code to create the user and log them in for each scenario.

With fixtures, all this is a thing of the past!

The fixture will contain all the code you need to create the user and log them into the application. Then, any test that needs a logged-in user can call it. This will help to simplify the configuration of your test environment and save time when creating tests.

Now you’ve seen what fixtures can do, let’s create some.

Create a Fixture

It’s pretty easy to create a fixture. If you know how to create a function, you know how to create a fixture. You just need one additional step to add a decorator above the function. Thanks, Pytest, for making it so easy for us! 😉

Have a look at this example of a fixture that builds a dictionary using some data and then returns the dictionary:

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "surname" : "Gonnage"}
    return data

Steps required:

  1. Import  pytest .

  2. Add the  @pytest.fixture  decorator.

  3. Define the function using the fixture name and the relevant data.

  4. Return all of the data.

Now that you know how to create a fixture, we’re going to see how we call them.

Call a Fixture

Let’s look at the fixture that we created earlier and see how to declare our test using the  first_fixture  fixture:  test_with_first_fixture(first_fixture) .

After declaring the name of the test, I declared the  first_fixture  fixture as an argument so that the configured data can be accessed. Now you have access to all of the data items returned by the fixture.

Once you run the test, Pytest will actually examine the arguments and check to see if a fixture exists with the same name as the parameters passed as arguments. In our case, it will look for a fixture called  first_fixture  and, if it finds one, it will capture the value returned by the fixture and provide the object as an argument for the test function. 

Okay, now for a little example to illustrate all of this theory:

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "surname" : "Gonnage"}
    return data

def test_with_first_fixture(first_fixture):
    print(first_fixture)
    assert first_fixture["first_name"] == "Ranga"
    assert first_fixture["surname"] == "Gonnage"
The fixture provides a dictionary that contains two elements which can be used as part of the test.
Call a fixture

You might have noticed in the above image that when the test is run, the  first_fixture  argument contains the dictionary returned by the fixture. So, the  first_fixture  argument equals  {'first_name': 'Ranga', 'surname': 'Gonnage'} .

Here’s a screencast showing you these steps in detail. You’ll see how to create your fixture and how to use it in your testing:

Reuse a Fixture

You haven’t seen why it’s useful to create fixtures yet. One of the most powerful aspects when using fixtures is the ability to create a generic configuration and reuse it for several tests. This means that you can call the same fixture for two different, independent tests.

So, if we take the previous example, we can create a second test that will call the  first_fixture  fixture:

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "surname" : "Gonnage"}
    return data

def test_fixture(first_fixture):
    assert first_fixture["first_name"] == "Ranga"
    assert first_fixture["surname"] == "Gonnage"

def test_fixture(first_fixture):
    assert first_fixture["first_name"] == "Ranga"
    assert first_fixture["surname"] == "Gonnage"

For a project that uses the Flask or Django framework, you need to instantiate a client to create a test web browser. You can make use of fixtures to make the client configuration easier. So, you’ll call the fixture for each of your tests.

For example, within the Flask framework:

import pytest
from myapp import create_app

@pytest.fixture
def client():
    app = create_app({"TESTING": True})
    with app.test_client() as client:
        yield client

And for the Django framework:

import pytest
from django.test import Client

@pytest.fixture
def client():
    c = Client()
    return c 

Reuse a Fixture in a Fixture

One final point to finish off this part about fixtures. In some test scenarios, you might need additional data or a different configuration for a test to be successful.

But this data might only be useful for one test. It’s not advisable to add this data item to the main fixture, but instead you can add another fixture, which will call the main fixture with the extra data item.

As you can tell, Pytest is a truly flexible test framework. This is how it enables you to call a fixture within a fixture.

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "surname" : "Gonnage"}
    return data

@pytest.fixture
def second_fixture(first_fixture):
    first_fixture["email"] = "test@test.com"
    return first_fixture

def test_fixture(second_fixture):
    assert second_fixture["first_name"] == "Ranga"
    assert second_fixture["surname"] == "Gonnage"
    assert second_fixture["email"] == "test@test.com"

The second fixture  second_fixture  supplements the first fixture  first_fixture  with a new element in the dictionary. So, in this second fixture, we are adding a data item with a key of  email  and a value of  test@test.com .

The  test_fixture  test shows that we also have access to the data item with  email  as its key. Go ahead and try out this section of code and you’ll see that the test works.

Over to You!

Right, we’ve almost finished with fixtures. Go back to your Django project (OC-Commerce) that we’ll be working on in the next part of the course.

Your Mission:

  • Add fixtures to simplify your testing.

You could for example configure a logged-in user, some mocks, or you could even initialize the database.

Find a suggested solution on GitHub!

Let’s Recap!

  • Creating fixtures means that you can have a controlled environment to execute your testing so that results are always reproducible.

  • The  @pytest.fixture  decorator converts a simple function into a fixture.

  • To use a fixture in your testing, it must be defined as a test argument and it can be used as many times as you need.

  • A fixture can call another to provide additional configuration.

Now that you understand the concept of a fixture, you know how to configure a unit test without having to duplicate code. However, this isn’t the only solution. We’re going to see how to organize testing into classes to simplify test creation.

Example of certificate of achievement
Example of certificate of achievement