Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In py.test, what's the point of marking fixture as fixture?

Tags:

python

pytest

import pytest

@pytest.fixture()
def my_fixture():
    data = {'x': 1, 'y': 2, 'z': 3}
    return data

def test_my_fixture(my_fixture):
    assert my_fixture['x'] == 1

What's the benefit of marking my_fixture as a pytest fixture? It seems like the fixture's benefit is the same with my_fixture just being a normal function, removing the decorator.

I see the benefit only here where my_fixture is run simply by providing it as a parameter to test_my_fixture:

@pytest.fixture()
def my_fixture():
    print "\nI'm the fixture"

def test_my_fixture(my_fixture):
    print "I'm the test"

This should print:

I'm the fixture
I'm the test
like image 842
imagineerThat Avatar asked Oct 09 '17 20:10

imagineerThat


People also ask

What is the use of fixture function in tests?

Therefore, instead of running the same code for every test, we can attach fixture function to the tests and it will run and return the data to the test before executing each test. A test function can use a fixture by mentioning the fixture name as an input parameter.

What is a fixture in pytest?

One of the things that makes pytest’s fixture system so powerful, is that it gives us the ability to define a generic setup step that can be reused over and over, just like a normal function would be used. Two different tests can request the same fixture and have pytest give each test their own result from that fixture.

How do I use fixtures in pytestmark?

@pytest.mark.usefixtures("cleandir", "anotherfixture") def test(): ... and you may specify fixture usage at the test module level using pytestmark: It is also possible to put fixtures required by all tests in your project into an ini-file: Note this mark has no effect in fixture functions. For example, this will not work as expected:

How to reuse fixtures in pytest parametrized tests?

Being able to reuse fixtures in parametrized tests is a must when we want to avoid repetition. Unfortunately, pytest doesn’t support that yet. On the other hand, we can make it happen either by using getfixturevalue in pytest or through a third-party library.


1 Answers

If you do not declare my_fixture as a fixture, then the test will not be able to use it as a fixture:

import pytest

def my_fixture():
    data = {'x': 1, 'y': 2, 'z': 3}
    return data

def test_my_fixture(my_fixture):
    assert my_fixture['x'] == 1

This script leads to an error:

  def test_my_fixture(my_fixture):
E       fixture 'my_fixture' not found
>       available fixtures: cache, capfd, capsys, doctest_namespace, monkeypatch, pytestconfig, record_xml_property, recwarn, tmpdir, tmpdir_factory
>       use 'pytest --fixtures [testpath]' for help on them.

The purpose of the fixtures is to prepare something before the test(s). And to kill it afterward. And NOT to be part of the test (e.g. in logging, reporting, ways of exception handling, etc). It is kind of a pre-condition, created by something else, and just given to the test.

If you just declare it as a function, and use it as a function, it is a function, not a fixture. And its failures become the failures of the test, not the general framework run.

Consider this example:

import pytest

@pytest.fixture()
def my_fixture():
    data = {'x': 1, 'y': 2, 'z': 3}
    print('prepare')
    yield data
    print('destroy')

def test_my_fixture(my_fixture):
    print('test it')
    assert my_fixture['x'] == 1

Also in this example try uncommenting the raising lines, and see the difference. It will be ERROR vs FAILURE. It is critical for how the test results are interpreted and handled later (e.g., by the developers, QA engineers, or whoever analyses the test results).

import pytest

@pytest.fixture()
def my_fixture():
    print('prepare')
    # raise Exception('Oops')
    yield None
    print('destroy')

def test_my_fixture(my_fixture):
    # raise Exception('Booms!')
    print('test it')

In the fixture:

======================================= test session starts ========================================
collected 1 item                                                                                    

t.py E

============================================== ERRORS ==============================================
________________________________ ERROR at setup of test_my_fixture _________________________________

    @pytest.fixture()
    def my_fixture():
        data = {'x': 1, 'y': 2, 'z': 3}
        print('prepare')
>       raise Exception('Oops')
E       Exception: Oops

t.py:7: Exception
===================================== 1 error in 0.03 seconds ======================================

In the test or any function called from the test:

======================================= test session starts ========================================
collected 1 item                                                                                    

t.py F

============================================= FAILURES =============================================
_________________________________________ test_my_fixture __________________________________________

my_fixture = {'x': 1, 'y': 2, 'z': 3}

    def test_my_fixture(my_fixture):
>       raise Exception('Booms!')
E       Exception: Booms!

t.py:12: Exception
===================================== 1 failed in 0.03 seconds =====================================

Also note that the fixtures have scopes: session, module, function. It will be difficult to demonstrate in a short example, but in that case, the fixture will be prepared and destroyed only once (usually only once) for multiple tests within that scope. This is important for the heavy-weight fixtures, such as the database pre-population. Such fixtures usually reside in the conftest.py pseudo-plugin in the directory structure (NB: this is not a regular module to be imported! by design, at least).

like image 151
Sergey Vasilyev Avatar answered Nov 10 '22 21:11

Sergey Vasilyev