Will
Will

Reputation: 4621

Why cant unittest.TestCases see my py.test fixtures?

I'm trying to use py.test's fixtures with my unit tests, in conjunction with unittest. I've put several fixtures in a conftest.py file at the top level of the project (as described here), decorated them with @pytest.fixture, and put their names as arguments to the test functions that require them.

The fixtures are registering correctly, as shown by py.test --fixtures test_stuff.py, but when I run py.test, I get NameError: global name 'my_fixture' is not defined. This appears to only occur when I use subclasses of unittest.TestCase—but the py.test docs seem to say that it plays well with unittest.

Why can't the tests see the fixtures when I use unittest.TestCase?


Doesn't work:

conftest.py

@pytest.fixture
def my_fixture():
    return 'This is some fixture data'

test_stuff.py

import unittest
import pytest

class TestWithFixtures(unittest.TestCase):

    def test_with_a_fixture(self, my_fixture):
         print(my_fixture)

Works:

conftest.py

@pytest.fixture()
def my_fixture():
    return 'This is some fixture data'

test_stuff.py

import pytest

class TestWithFixtures:

    def test_with_a_fixture(self, my_fixture):
         print(my_fixture)

I'm asking this question more out of curiosity; for now I'm just ditching unittest altogether.

Upvotes: 31

Views: 29642

Answers (4)

rustinpeace91
rustinpeace91

Reputation: 99

This is what worked for me. Someone with better knowledge of pytest might be able to explain why it works. I could not get the solution in the documentation of using @pytest.mark.usefixtures on the class to work for me.

class MyTest(TestCase):

    @pytest.fixture(autouse=True)
    def inject_fixtures(self, request):
        self.db_session = request.getfixturevalue("db_session")

    def test_method1(self):
        assert hasattr(self, "db_session")
        # assert 0, self.db  # fail for demo purposes

Edit: the reason the mark.usefixtures solution wasn't working for me is because I wasn't reading the example in the documentation carefully enough. To get it to work you need to modify your fixture to have a scope of class and to actually set the attribute on the class like this

# scope of class
@pytest.fixture(scope="class")
def db_class_session(setup_database, db_connection, request):
    
    # set the actual attribute on the class
    request.cls.db_class_session = db_connection

then it should work like this in your test

@pytest.mark.usefixtures("db_class_session")
class MyTest(TestCase):
    def test_method1(self):
        assert hasattr(self, "db_class_session")
        import pdb; pdb.set_trace()
        # assert 0, self.db  # fail for dem

Hopefully this helps. I think I prefer the first inject_fixtures function solution more because it means I do not have to refactor or modify a fixture that is being used in other tests, but someone with more knowledge on pytest can feel free to explain to me why it is or isn't the better solution

Upvotes: 0

Radhwane Chebaane
Radhwane Chebaane

Reputation: 864

You can use the pytest fixtures in unittest.TestCase with the pytest option autouse. However, if you use the test_ for the unit method using the fixture the following error will appear:

Fixtures are not meant to be called directly,...

### conftest.py
@pytest.fixture
def my_fixture():
    return 'This is some fixture data'

One solution is to use a prepare_fixture method to set fixtures as an attribute of the TestWithFixtures class, so that fixtures are available to all unit test methods.

### test_stuff.py
       
import unittest
import pytest
    
class TestWithFixtures(unittest.TestCase):
    

    @pytest.fixture(autouse=True)
    def prepare_fixture(self, my_fixture):
        self.myfixture = my_fixture

    def test_with_a_fixture(self):
        print(self.myfixture)

Upvotes: 11

PillowDaddy
PillowDaddy

Reputation: 61

  1. define the fixture as an accessible variable, (like input in the following example). To define it, use request.cls.VARIABLE_NAME_YOU_DEFINE = RETURN_VALUE

  2. use @pytest.mark.usefixtures("YOUR_FIXTURE") to use fixture outside of the unittest class, inside the unittest class, access the fixture by self.VARIABLE_NAME_YOU_DEFINE.

e.g.

import unittest
import pytest


@pytest.fixture(scope="class")
def test_input(request):
    request.cls.input = {"key": "value"}


@pytest.mark.usefixtures("test_input")
class MyTestCase(unittest.TestCase):

    def test_something(self):
        self.assertEqual(self.input["key"], "value")

Upvotes: 6

Frank T
Frank T

Reputation: 9046

While pytest supports receiving fixtures via test function arguments for non-unittest test methods, unittest.TestCase methods cannot directly receive fixture function arguments as implementing that is likely to inflict on the ability to run general unittest.TestCase test suites.

From the note section at the bottom of: https://pytest.org/en/latest/unittest.html

It's possible to use fixtures with unittest.TestCasees. See that page for more information.

Upvotes: 25

Related Questions