How to detect when pytest test case got AssertionError? - python

I am using pytest to automate project test. I want to take some unique actions like "save_snapshot()" only when a test case fails.
Do we have something like that in pytest?
I have tried to achieve this using the teardown_method() But this method is not getting executed when a test case fails.
without using fixtures please.

I found a solution for this issue by using python decorator for each test in class:
def is_failed_decorator(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except AssertionError:
cls_obj = args[0]
cur_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
func_name = func.__name__
# Save_snapshot().
raise
return wrapper
# Tests class
#is_failed_decorator
def test_fail(self):
assert False
worked for me :D

Related

how to write Unittets for a method which is returning a method

def acquisition_required(method):
def wrapped_method(self, *args, **kwargs):
result=some complex code
if not result:
some code is here
else:
return method(self, *args, **kwargs)
return wrapped_method
I would like to write a Unittest for this
eg.
assertEqual, assertTrue..
But i don't know how to test it i have done unittesting for function returning some values or True/False.
I don't want any code just concept
If your function will be success then the return value will be the reference of getting method. It means you can use the assertEqual method of unittest module. Like below:
import unittest
import your_module
class ConfigParserTestCases(unittest.TestCase):
def test_return_method(self):
self.assertEqual(your_module.acquisition_required(method), method)
if __name__ == "__main__":
unittest.main()
Note: If your implementation contains Exceptions (Eg.: try-except for error handling), you can test the Exception case with "assertRaises" method of unittest module. From the Official Python documentation.
Eg. in your case (if you want to test ValueError exception):
with self.assertRaises(ValueError):
self.assertRaises(your_module.acquisition_required(method))
I manged to do this as below:
with self.assertRaises(ValueError):
self.assertRaises(your_module.acquisition_required(method))

Is it possible to run a single test method from a python unittest.TestCase with a reference to the method?

Suppose I have the following TestCase
class TestSomething(unittest.TestCase):
def test_a(self):
# Do some testing
def test_b(self):
# Do some other testing
Is it possible for me to run TestSomething.test_a if I have a reference to that test? What I am looking to do is something like:
def run_test(test):
# Somehow runs the test
# HERE IS THE PART I AM REALLY STUCK ON
run_test(TestSomething.test_a)
I know that it's an awkward thing to do for normal unit testing. What I am trying to do is provide a test to be run as an argument to a function decorator. Essentially:
#corresponding_test(TestSomething.test_a)
def my_function_a():
# Something here
And then in the decorator basically check if the test for that function passes before running the function.
OP clearly stated that the real world use case is more involved, but this still needs saying:
Disclaimer: This is not a good, standard way to run unit tests. If you use this code to run unit tests, you're [probably] doing it wrong.
That said, your question intrigued me, so I went ahead and wrote a working demo for you:
"""
The `only_if_test_passes` decorator can be used to run a function if and
only if the argument test (unbound `TestCase` method) passes.
"""
import inspect
from unittest import TestCase, TestResult
class TestError(Exception):
pass
class MyTests(TestCase):
def test_pass(self):
# This passes because nothing went wrong
pass
def test_fail(self):
self.fail('This test will always fail')
def only_if_test_passes(test_method):
# Comments are computed values when passed MyTests.test_pass
test_case_class = inspect._findclass(test_method) # MyTests
test_case_name = test_case_class.__name__ # 'MyTests'
test_name = test_method.__name__ # 'test_pass'
# Introspection for Python 2:
# test_case_class = test_method.im_class
# test_case_name = test_case_class.__name__ # Same as for Python 3
# test_name = test_method.if_func.func_name
def decorator(fn):
def decorated(*args, **kwargs):
test_result = TestResult()
case = test_case_class(test_name) # MyTests('test_pass')
case(test_result)
if test_result.wasSuccessful():
return fn(*args, **kwargs)
else:
raise TestError('Unit test failed: {}.{}'.format(
test_case_name, test_name))
return decorated
return decorator
#only_if_test_passes(MyTests.test_pass)
def this_will_run():
print('This should output')
#only_if_test_passes(MyTests.test_fail)
def this_wont_ever_run():
print("Don't bother; you'll never see this.")
if __name__ == "__main__":
this_will_run()
this_wont_ever_run()
gist
The introspection will be a little different in Python 2.
See also: unittest.TestCase docs

Mock patch decorators python

Hi I would like to mock my decorator since I don't want to be actually calling/executing this function. But I can't seem to find the solution for this below are my code
# This is the decorator located in my project
# This is located in custom.mydecorators.decorator_file.custom_decorator
Base = declarative_base()
def custom_decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
print("This message is still printed even when I try to patch this function")
try:
#code here
my_var = CoolClass()
retval = func(*args, **kwargs)
except Exception as e:
#rollback code here
raise e
return retval
return wrapper
Now I'm trying to patch this using this code
patch('custom.mydecorators.decorator_file.custom_decorator', lambda x: x).start()
class TestMockDecoratorsCallingClass(unittest.TestCase):
def test_should_return_success_if_decorators_are_mocked(self):
# Code here
My decorators work properly in a non unittest file. But if I mock this decorator it fails saying that the local variable 'my_var' referenced before assignment
Note: my_var is inside the decorator I'm trying to mock/patch also the print message is still executed even when I try to patch it

pytest how to take actions on setup failure

I am trying to find a clean way to execute some code when the setup_class method of py.test fails. Given the following example:
class TestClass:
#classmethod
def setup_class(self):
print("I am performing setup actions!")
def test_one(self):
x = "this"
assert 'h' in x
def setup_cleanup(self):
print("I want to perfrom some cleanup action!")
How do I get the setup_cleanup method to be triggered by py.test if setup raises an exception?
You can use this kind of decorator catch exceptions in the setup_class method:
def is_failed_decorator(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except AssertionError:
# Your code here.
raise
return wrapper
#is_failed_decorator
#classmethod
def setup_class(self):

PyTest skip module_teardown()

I have following code in my tests module
def teardown_module():
clean_database()
def test1(): pass
def test2(): assert 0
and I want teardown_module() (some cleanup code) to be called only if some test failed. Otherwise (if all passed) this code shouldn't have to be called.
Can I do such a trick with PyTest?
You can. But it is a little bit of a hack.
As written here: http://pytest.org/latest/example/simple.html#making-test-result-information-available-in-fixtures
you do the following, to set up an attribute for saving the status of each phase of the testcall:
# content of conftest.py
import pytest
#pytest.mark.tryfirst
def pytest_runtest_makereport(item, call, __multicall__):
rep = __multicall__.execute()
setattr(item, "rep_" + rep.when, rep)
return rep
and in the fixture you just examine the condition on those attributes like this:
import pytest
#pytest.yield_fixture(scope="module", autouse=True)
def myfixture(request):
print "SETUP"
yield
# probably should not use "_collected" to iterate over test functions
if any(call.rep_call.outcome != "passed" for call in request.node._collected):
print "TEARDOWN"
This way if any of the tests associated with that module fixture is not "passed" (so "failed" or "skipped") then the condition holds.
The answer posted here and link to documentation was helpful but not sufficient for my needs. I needed a module teardown function to execute for each module independently if any test in that module (.py) file failed.
A complete sample project is available on GitHub
To start with, we need a hook to attach the test function result to
the test node. This is taken directly from the pytest docs:
# in conftest.py
#pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
outcome = yield
rep = outcome.get_result()
# set a report attribute for each phase of a call, which can
# be "setup", "call", "teardown"
var_name = "rep_" + rep.when
setattr(item, var_name, rep)
After that, we need another hook for the test case to find the module and
store itself there, so the module can easily find its test cases. Perhaps
there's a better way, but I was unable to find one.
# also in conftest.py
#pytest.fixture(scope="function", autouse=True)
def _testcase_exit(request):
yield
parent = request.node.parent
while not isinstance(parent, pytest.Module):
parent = parent.parent
try:
parent.test_nodes.append(request.node)
except AttributeError:
parent.test_nodes = [request.node]
Once we do that, it's nice to have a decorator function to have the module on
completion look through its test nodes, find if there are any failures, and
then if there were call the function associated with the decorator:
# also also in conftest.py
def module_error_teardown(f):
#wraps(f)
#pytest.fixture(scope="module", autouse=True)
def wrapped(request, *args, **kwargs):
yield
try:
test_nodes = request.node.test_nodes
except AttributeError:
test_nodes = []
something_failed = False
for x in test_nodes:
try:
something_failed |= x.rep_setup.failed
something_failed |= x.rep_call.failed
something_failed |= x.rep_teardown.failed
except AttributeError:
pass
if something_failed:
f(*args, **kwargs)
return wrapped
Now we have all the necessary framework to work with. Now, a test file with a failing test case is easy to write:
from conftest import module_error_teardown
def test_something_that_fails():
assert False, "Yes, it failed."
def test_something_else_that_fails():
assert False, "It failed again."
#module_error_teardown
def _this_gets_called_at_the_end_if_any_test_in_this_file_fails():
print('')
print("Here's where we would do module-level cleanup!")

Categories