Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using fixture decorator as a function #2001

Closed
westhomas opened this Issue Oct 13, 2016 · 4 comments

Comments

Projects
None yet
2 participants
@westhomas
Copy link
Contributor

westhomas commented Oct 13, 2016

I have an autogenerated unit test class that I'd like to decorate with a fixture for some injected functionality. Knowing that decorators are "just functions" led me to something like this...

import logging
import random
import unittest
from pytest import fixture, mark

logger = logging.getLogger(__name__)

@fixture(scope='class')
def unique_bed(request, worker_id):
    request.cls.bed = random.randint(1, 12)

def _build_config_test_case_class():
    test_case_class = type('TestConfig', (unittest.TestCase,), {})

    def test_method(cls):
        logger.debug(cls.__class__)
        logger.debug(cls.bed)

    setattr(test_case_class, "test1", test_method)
    setattr(test_case_class, "test2", test_method)
    setattr(test_case_class, "test3", test_method)

    return mark.usefixtures(test_case_class, "unique_bed")
    #return test_case_class #Returning the class works as expected

ConfigTestCase = _build_config_test_case_class()

However this leads to pytest being unable to find the "unittest.TestCase" base classes.

Perhaps my python-fu is just not strong enough to see the obvious answer, but what's the solution here? How can I keep my existing autogenerated test classes and add a fixture decorator programmatically?

(Editted by @nicoddemus: updated syntax highlight)

@nicoddemus

This comment has been minimized.

Copy link
Member

nicoddemus commented Oct 14, 2016

Hi, sorry for the delay.

I think the simplest solution is to decorate your class with the usefixtures marker:

@pytest.mark.usefixtures('unique_bed')
class TestConfig(unittest.TestCase):
    ...
@westhomas

This comment has been minimized.

Copy link
Contributor Author

westhomas commented Oct 14, 2016

But I don't have a class declaration. It's generated using type().

@nicoddemus

This comment has been minimized.

Copy link
Member

nicoddemus commented Oct 14, 2016

Oh my bad, completely missed this line:

return mark.usefixtures(test_case_class, "unique_bed")

If you add this to your test case:

print(ConfigTestCase)

You will see that it is not really the class being returned, but a MarkDecorator instance:

<MarkDecorator 'usefixtures' {'args': (<class 'foo.TestConfig'>, 'unique_bed'), 'kwargs': {}}>

What's happening is:

A decorator is a syntax sugar, so the two are equivalent:

@dec
class T:
    pass

# Equivalent to:
T = dec(T)

A decorator with an argument is a little different in that it should return a callable which is then applied to the class:

@dec('param')
class T:
    pass

# Equivalent to:
T = dec('param')(T)

So you have to change your example to:

return mark.usefixtures("unique_bed")(test_case_class)
@westhomas

This comment has been minimized.

Copy link
Contributor Author

westhomas commented Nov 1, 2016

This was very helpful. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.