Is it possible to disable fixtures in pytest?
The reason I want to do that is that I'm using my own fixtures framework, currently like this (the pros and cons of different fixtures mechanisms isn't the focus of this question):
import functools
def with_fixtures(test_func):
#functools.wraps(test_func)
def wrapper(self, *args, **kwargs):
# This simplified code reproduces the problem -- the real implementation passes
# an object instead of a dummy
return test_func(self, "dummy fixtures", *args, **kwargs)
return wrapper
class Test:
#with_fixtures
def test(self, fixtures):
print("fixtures %s" % (fixtures, ))
pass
If I run that test with another framework, my with_fixtures decorator passes a fixtures object to the test. If I run pytest on that, I get this:
def test(self, fixtures):
E fixture 'fixtures' not found
In order to disable pytest fixtures, I'd rather locally mark individual tests with a decorator than add code to a special file like conftest.py on which my tests have no explicit dependency, so that's it's easier to see locally why the test behaves as it does.
if you want to hide your arguments from pytest, use a signature objects from funcsigs/inspect that tells pytest the funcion has no arguments
alternatively have own test items that dont use the fixture system
pytest currently has no support for replacing the fixture system, so you will have to fight it
PyTest uses inspect.signature, which can be overwritten by __signature__. To remove an argument from the signature, an easy way is to get the signature of
functools.partial(func, None) (consumes the first positional argument) or
functools.partial(func, kw=None) (consumes keyword argument kw).
def with_fixtures(test_func):
#functools.wraps(test_func)
def wrapper(self, *args, **kwargs):
# This simplified code reproduces the problem -- the real implementation passes
# an object instead of a dummy
return test_func(self, "dummy fixtures", *args, **kwargs)
wrapper.__signature__ = inspect.signature(functools.partial(test_func, None))
return wrapper
See https://github.com/cupy/cupy/pull/4192/files#diff-0325229b89e681b528114e65b1f5a3369be70bb4fbcbb441a9a1bdd7de60be51 for a real-world example.
Related
Let's say I have a very simple logging decorator:
from functools import wraps
def my_decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
print(f"{func.__name__} ran with args: {args}, and kwargs: {kwargs}")
result = func(*args, **kwargs)
return result
return wrapper
I can add this decorator to every pytest unit test individually:
#my_decorator
def test_one():
assert True
#my_decorator
def test_two():
assert 1
How can I automatically add this decorator to every single pytest unit test so I don't have to add it manually? What if I want to add it to every unit test in a file? Or in a module?
My use case is to wrap every test function with a SQL profiler, so inefficient ORM code raises an error. Using a pytest fixture should work, but I have thousands of tests so it would be nice to apply the wrapper automatically instead of adding the fixture to every single test. Additionally, there may be a module or two I don't want to profile so being able to opt-in or opt-out an entire file or module would be helpful.
Provided you can move the logic into a fixture, as stated in the question, you can just use an auto-use fixture defined in the top-level conftest.py.
To add the possibility to opt out for some tests, you can define a marker that will be added to the tests that should not use the fixture, and check that marker in the fixture, e.g. something like this:
conftest.py
import pytest
def pytest_configure(config):
config.addinivalue_line(
"markers",
"no_profiling: mark test to not use sql profiling"
)
#pytest.fixture(autouse=True)
def sql_profiling(request):
if not request.node.get_closest_marker("no_profiling"):
# do the profiling
yield
test.py
import pytest
def test1():
pass # will use profiling
#pytest.mark.no_profiling
def test2():
pass # will not use profiling
As pointed out by #hoefling, you could also disable the fixture for a whole module by adding:
pytestmark = pytest.mark.no_profiling
in the module. That will add the marker to all contained tests.
I have a context manager for an object that can be used similar to the open context manager, e.g.
with MyContextManager as cm:
cm.do_something()
I know that a simple context manager can be made into a decorator if using contextlib.ContextDecorator to create it. Is it also possible to access the object yielded from the context manager if using a decorator? E.g. given the context manager above, something like:
#cmdecorator
def my_function(self, cm):
cm.do_something
I couldn't get that to work. Either I'm missing something trivial (hope so), or this is just not possible... It is only syntactic sugar in the end, but I'm interested if it is possible.
No. This is explicitly mentioned in the documentation.
Note that there is one additional limitation when using context managers as function decorators: there’s no way to access the return value of __enter__(). If that value is needed, then it is still necessary to use an explicit with statement.
To answer my own question: while there is no automatic mechanism for this, it is easy enough to write a decorator that does exactly this (using a hardcoded keyword argument):
def patch_cm(f):
#functools.wraps(f)
def decorated(*args, **kwargs):
with MyContextManager() as cm:
kwds['cm'] = cm
return f(*args, **kwargs)
return decorated
which can be used (here in a unittest):
class MyContextManagerTest(TestCase):
#patch_cm
def test_context_decorator(self, cm):
cm.do_something()
self.assertTrue(cm.done())
which is the same as:
def test_context_decorator(self):
with MyContextManager() as cm:
cm.do_something()
self.assertTrue(cm.done())
(what I actually use is a wrapper with arguments, but that is just one more wrapper around...)
This is also possible with a positional argument instead of a keyword argument (which I eventually ended up using):
def patch_cm(f):
#functools.wraps(f)
def decorated(*args, **kwargs):
with MyContextManager() as cm:
args = list(args)
args.append(cm)
return f(*args, **kwargs)
return decorated
I'm looking to add a decorator that runs a video recorder on certain tests like so:
#decorators.video(self)
def test_1234(self):
...
I'm having trouble passing the self variable into the decorator as it is need for some attributes. How can I do this?
theodox answer is generally good, but for decorators you should use functools.wraps function, like in an example below:
from functools import wraps
def enable_video(fn)
'''Decorate the function to start video, call the function, stop video.'''
#wraps(fn)
def inner(*args, **kwargs):
# could be just `def inner(self):` if only intended to use
# with methods without arguments and keyword arguments
do_stuff_before()
fn(*args, **kwargs)
do_stuff_after()
return inner
It will persist original docstrings, original function name (and more). You can read more about it in Python docs.
Then, assuming that previous code is in decorators module, you should use it as follows:
class MyTestCase(unittests.TestCase);
#decorators.enable_video
def testSomeVideoFunction(self):
do_test_stuff()
Note that in the code example it's just #decorators.enable_video, not #decorators.enable_video(self). As like in jonrsharpe's comment to your question, reference to a self is not present at a decoration time.
Are you sure you need the self reference at all?
More commonly you'd do something like this
def enable_video(fn):
'''decorate the test function so it starts the video, runs the test, and stop the video'''
def video_aware_test(self_refrence):
start_video_recorder()
try:
fn()
finally:
stop_video_recorder()
return video_aware_test
And you'd apply it like this:
#enable_video
def test_something(self)
If for some reason the decorator actually needed the self reference, you can see where you'd grab it. This version doesn't include configuring the video recorder in any way, to that you'd use a class rather than a function decorator and pass the configuration as arguments.
I'm writing a TestCase in Python using unittest, that looks something like this:
class MyTestCase(unittest.TestCase):
def setUp(self):
# ... check if I'm online - might result in True or False
self.isOnline = True
#unittest.skipIf(not self.isOnline, "Not online")
def test_xyz(self):
# do a test that relies on being online
However, this doesn't seem to work, I assume because #skipIf cannot use self outside the body of the function declaration. I know I can check the value of self.isOnline inside the test_xyz function and use skipTest instead, but this is less elegant. Are there any other options?
You could write your own decorator to which you pass the name of the flag:
def skipIfTrue(flag):
def deco(f):
def wrapper(self, *args, **kwargs):
if getattr(self, flag):
self.skipTest()
else:
f(self, *args, **kwargs)
return wrapper
return deco
Then in your class you would define the test method like this:
#skipIfTrue('isOnline')
def test_thing(self):
print("A test")
Whether this is better than just checking in the method depends on the situation. If you are doing this with many methods, it could be better than writing the check in every one. On the other hand, if you're doing that, you might want to group them together and do one check to skip the entire suite.
If you can move the isOnline test outside of your setUp method, then that's a solution:
IS_ONLINE = i_am_online()
class MyTestCase(unittest.TestCase):
#unittest.skipUnless(IS_ONLINE, "Not online")
def test_xyz(self):
# do a test that relies on being online
Another (more elegant option) would then be:
import unittest
def skipWhenOffline():
if not i_am_online():
return unittest.skip("Not online")
return unittest._id
class MyTestCase(unittest.TestCase):
#unittest.skipWhenOffline()
def test_xyz(self):
# do a test that relies on being online
However if this isn't possible for you, then there isn't a more elegant solution than to somehow use skipTest().
skipIf and skipUnless are evaluated at class declaration time (they set a __unittest_skip__ attribute on your method to indicate it shouldn't run, which is later looked at prior to running the test). Your setUp method just hasn't run yet at that point.
All the other answers here can work, but they more or less a hack and/or need 3rd party solutions.
Might as well go with the old school way to just put if condition: self.skipTest() as the first line in your test function.
def test_xyz(self):
if not self.isOnline:
self.skipTest("Not online")
## You could even write it as a one-liner, should you prefer
# if not self.isOnline: self.skipTest("Not online")
## Which happens to be exact the same length as the decorator,
## so it is roughtly the same as decorator even from the convenience standpoint
# #unittest.skipIf(not self.isOnline, "Not online")
do_a_test_that_relies_on_being_online(...)
I would add to the answer by BrenBarn that if you're using nosetests that you'll need to decorate the wrapper function with #wraps. Without this, the test function would technically be wrapper and this would not be invoked by nosetests, as by default test methods need to start with test_.
from unittest2.compatibility import wraps
def skipIfTrue(flag):
def deco(f):
#wraps(f)
def wrapper(self, *args, **kwargs):
if getattr(self, flag):
self.skipTest()
else:
f(self, *args, **kwargs)
return wrapper
return deco
The Twython module uses requests module internally.
I want to wrap/decorate request's requests.post(*k, **kw) method so everything Twython makes a request.post(...) call it will be transparently decorated/wrapped without interfering with the Twython module.
If I edited the requests codebase that'd be easy enough but I'm curious how to solve the general problem of adding a decorator to an already defined function/method.
import requests
def magic_wrapper_doodad(...)
...
...
requests.post = magic_wrapper_doodad(my_function, requests.post) # plz?
import Twython
# thanks to the above magic, requests.post is wrapped just as if it was defined like:
#decorator
def trace(f, *args, **kw):
print("calling %s with args %s, %s" % (f.__name__, args, kw))
return f(*args, **kw)
...
... #inside requests.py now:
#trace
def post(self, *args, **kw):
...
how do I write magic_wrapper_doodad() - or some alternative code - so I can decorate the code like this?
Decorator # notation is just syntactic sugar for calling the decorator callable with the decorated function and replacing the decorated function with the result.
In other words, the following two examples are the exact same thing:
#yourdecorator
def a_function():
pass
def b_function():
pass
b_function = yourdecorator(b_function)
and the same applies to methods in class definitions.
Thus, you can simply decorate a class method by replacing the class method with the result of the decorator callable:
requests.post = my_function(requests.post)
This is the same as if you had written:
class requests(object):
#my_function
def post(self, *args):
pass
Note that in your case requests is actually a module, not a class, but the same principles apply.
Martijn Pieters' answer is the way to go if you need to decorate the requests function. I just thought I'd mention that requests does actually have a mechanism for hooks.
If you can do what you need to do with a hook instead, you should. It's cleaner than monkey patching.
What you are looking for might be done with aspect oriented programming.
Check this post from Stackoverflow and please let me know if it helps:
Do you use AOP (Aspect Oriented Programming) in production software?