Skip to content

Using fixture decorator as a function #2001

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

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

Using fixture decorator as a function #2001

westhomas opened this issue Oct 13, 2016 · 4 comments
Labels
type: question general question, might be closed after 2 weeks of inactivity

Comments

@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
Copy link
Member

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):
    ...

@nicoddemus nicoddemus added the type: question general question, might be closed after 2 weeks of inactivity label Oct 14, 2016
@westhomas
Copy link
Contributor Author

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

@nicoddemus
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
Copy link
Contributor Author

This was very helpful. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question general question, might be closed after 2 weeks of inactivity
Projects
None yet
Development

No branches or pull requests

2 participants