I have 2 modules: test1.py and test2.py.
test1.py
import unittest
class ArithTest (unittest.TestCase):
def test_run (self):
""" Test addition and succeed. """
self.failUnless (1+1==2, 'one plus one fails!')
self.failIf (1+1 != 2, 'one plus one fails again!')
self.failUnlessEqual (1+1, 2, 'more trouble with one plus one!')
if __name__ == '__main__':
unittest.main()
test2.py
import unittest
class AlgTest (unittest.TestCase):
def test_alg (self):
""" Test addition and succeed. """
self.assertEqual(1+1, 2, '1+1 != 2? whaaat?')
self.assertEqual(6-5, 1, '6-5 != 5 wft python?')
if __name__ == '__main__':
unittest.main()
-Now-
I wanna create a new module test3.py to test test1.py and test2.py. I don't now how, i read on internet about suit tests but i don't understand.
I don't want to create one more method with calling tests, and call them on test3.py.
I wanna group them and call in test3.py and they run as unitests
test1.py
import unittest
class ArithTest (unittest.TestCase):
def test_run (self):
""" Test addition and succeed. """
self.failUnless (1+1==2, 'one plus one fails!')
self.failIf (1+1 != 2, 'one plus one fails again!')
self.failUnlessEqual (1+1, 2, 'more trouble with one plus one!')
def runTest(self):
self.test_run()
if __name__ == '__main__':
unittest.main()
test2.py
import unittest
class AlgTest (unittest.TestCase):
def test_alg (self):
""" Test addition and succeed. """
self.assertEqual(1+1, 2, '1+1 != 2? whaaat?')
self.assertEqual(6-5, 1, '6-5 != 5 wft python?')
def runTest(self):
self.test_alg()
if __name__ == '__main__':
unittest.main()
test3.py
from .test1 import ArithTest
from .test2 import AlgTest
import unittest
def suite_2():
suite = unittest.TestSuite()
suite.addTest(ArithTest())
suite.addTest(AlgTest())
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
test_suite = suite_2()
runner.run(test_suite)
Also add a __init__.py
Run it with python3 -m folder_name.test3
Related
I combine mocking and caching in my code. The mocking is (kind of) random for each pytest as I do not know exactly, what will be returned in the real case. Hence I want to mock the very same function (in my example fct_child) with different values in different testcases. The caching makes some problems however, as the return values (in my example of fct_parent are cached and hence the mocked function is only reached in the first testcase and then always skipped due to the caching of the parent function. I need to find a way to clear/reset the cache between pytests.
In the following code the tests test_1 and test_2 can be executed independently of each over ($ pytest test_main.py::test_1 and $ pytest test_main.py::test_2), successfully. If pytest runs over the full module ($ pytest test_main.py), however, the second test crashes. Also the main part works ($ python test_main.py), where I ensure, that caching works as expected.
So how can I fix the code, such that pytest also passes when all testcases are executed (the $ pytest test_main.py scenario)?
test_main.py
# test_main.py
from my_lib import fct_parent, get_n_calls_fct_child
class ChildMock:
def __init__(self, val_child):
self.n_calls_mock = 0
self.val_child = val_child
def fct(self):
self.n_calls_mock += 1
return self.val_child
def test_1(monkeypatch):
"""This test interacts with test_2:
Exectuing each test independently with pytest works, executing both in one run, fails.
This is due to the lru_cache being not cleaned.
"""
val_child = "mocked test 1"
child_mock = ChildMock(val_child)
with monkeypatch.context() as mpc:
mpc.setattr("my_lib.fct_child", child_mock.fct) # mocks fct_child to return ret_val
assert fct_parent() == val_child
assert fct_parent() == val_child
assert child_mock.n_calls_mock == 1
def test_2(monkeypatch):
"""This test interacts with test_1:
Exectuing each test independently with pytest works, executing both in one run, fails.
This is due to the lru_cache being not cleaned.
"""
val_child = "mocked test 2"
child_mock = ChildMock(val_child)
with monkeypatch.context() as mpc:
mpc.setattr("my_lib.fct_child", child_mock.fct) # mocks fct_child to return ret_val
assert fct_parent() == val_child
assert fct_parent() == val_child
assert child_mock.n_calls_mock == 1
if __name__ == "__main__":
assert fct_parent() == "unmocked"
assert fct_parent() == "unmocked"
n_calls_fct_child = get_n_calls_fct_child()
assert n_calls_fct_child == 1, f"{n_calls_fct_child=} should be == 1"
print("good: fct_child was only computed once")
my_lib.py
# my_lib.py
from functools import lru_cache
_n_child_calls = 0
#lru_cache(256)
def fct_parent():
return fct_child()
def fct_child():
global _n_child_calls
_n_child_calls += 1
return "unmocked"
def get_n_calls_fct_child():
return _n_child_calls
just use pytest-antilru.
pip install pytest-antilru
and you are good to go
The following approach defines an #decorator, which clears the cache for the decorated function as soon as arriving in a new testcase.
my_lib_fixed.py
import os
from functools import lru_cache, wraps
_pytest_cache_func = {} # Dict {'func.__name__: name_of_pytest_with_last_caching}
_n_child_calls = 0
def lru_cache_pytest_save(*lru_cache_args, **lru_cache_kwargs):
"""like #lru_cache, but additionally clears lru_cache of this function in between pytest testcases"""
# if you want to switch _pytest_save off:
# def decorator(func):
# return lru_cache(func)
# return decorator
def decorator(func):
func_cached = lru_cache(func)
#wraps(func)
def wrapper(*args, **kwargs):
pytest_current = os.environ.get("PYTEST_CURRENT_TEST")
if _pytest_cache_func.get(func_cached.__name__) != pytest_current:
func_cached.cache_clear()
_pytest_cache_func[func_cached.__name__] = pytest_current
return func_cached(*args, **kwargs)
return wrapper
return decorator
#lru_cache_pytest_save(256)
def fct_parent():
return fct_child()
def fct_child():
global _n_child_calls
_n_child_calls += 1
return "unmocked"
def get_n_calls_fct_child():
return _n_child_calls
def reset_n_calls_fct_child():
global _n_child_calls
_n_child_calls = 0
as the modulename is slightly different, you need minor modifications in
test_main_fixed.py
# test_main_fixed.py
from my_lib_fixed import fct_parent, get_n_calls_fct_child
class ChildMock:
def __init__(self, val_child):
self.n_calls_mock = 0
self.val_child = val_child
def fct(self):
self.n_calls_mock += 1
return self.val_child
def test_1(monkeypatch):
"""This test interacts with test_2:
Exectuing each test independently with pytest works, executing both in one run, fails.
This is due to the lru_cache being not cleaned.
"""
val_child = "mocked test 1"
child_mock = ChildMock(val_child)
with monkeypatch.context() as mpc:
mpc.setattr("my_lib_fixed.fct_child", child_mock.fct) # mocks fct_child to return ret_val
assert fct_parent() == val_child
assert fct_parent() == val_child
assert child_mock.n_calls_mock == 1
def test_2(monkeypatch):
"""This test interacts with test_1:
Exectuing each test independently with pytest works, executing both in one run, fails.
This is due to the lru_cache being not cleaned.
"""
val_child = "mocked test 2"
child_mock = ChildMock(val_child)
with monkeypatch.context() as mpc:
mpc.setattr("my_lib_fixed.fct_child", child_mock.fct) # mocks fct_child to return ret_val
assert fct_parent() == val_child
assert fct_parent() == val_child
assert child_mock.n_calls_mock == 1
if __name__ == "__main__":
assert fct_parent() == "unmocked"
assert fct_parent() == "unmocked"
n_calls_fct_child = get_n_calls_fct_child()
assert n_calls_fct_child == 1, f"{n_calls_fct_child=} should be == 1"
print("good: fct_child was only computed once")
now all of the 4 commands work:
$ python test_main.py
$ pytest test_main_fixed.py::test_1
$ pytest test_main_fixed.py::test_2
$ pytest test_main_fixed.py
Here is my Code, I'm not sure if I lack or miss some code but it is not skipping the test2.
import unittest
class AppTesting (unittest.TestCase):
skipYes = ""
def test_1(self):
print("Test 1: " + str(AppTesting.skipYes))
AppTesting.skipYes = "Yes"
print("Test 1: " + str(AppTesting.skipYes))
#unittest.skipIf(str(skipYes) == "Yes", "Skip condition")
def test_2(self):
print("Test 2 skipYes is: " + str(AppTesting.skipYes))
print("This is Test 2")
def test_3(self):
print("This is Test 3")
if __name__ == "__main__":
unittest.main()
Your decorator is evaluated when the module is loaded on the test run together with the test class definition, hence the skipYes variable at that point will always be an empty string "", as defined in the class.
Changing this variable inside the test methods won't make any difference, as the test runner has already collected what tests to run and what to skip.
As a solution, from Python 3.4 you can use subtests:
class MyTestCase(unittest.TestCase):
def test_some(self):
skipTest = False
with self.subTest('subtest1'):
self.assertTrue(False)
...
if not skipTest:
with self.subTest('subtest2'):
self.assertFalse(True)
DOCS: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
I try to order my tests but the problem is that the test_add will be first and I need that the test_remove will be first. I try to use sortTestMethodsUsing but I still get test_add as the first test.
import unittest
class TestSum(unittest.TestCase):
def test_remove(self):
print("1")
def test_add(self):
print("2")
if __name__ == '__main__':
loader = unittest.TestLoader()
loader.sortTestMethodsUsing = None
unittest.main(testLoader=loader)
From two files test_now.py and test_later.py as follows:
# test_now.py
import unittest
class CommonClass(unittest.TestCase):
def hello(self):
print "Hello there"
def bye(self):
print "Bye"
def seeYouAgain(self):
print "See You Again"
def whatsUp(self):
print "What's up?"
def testNow(self):
self.hello()
self.bye()
if __name__ == '__main__':
unittest.main()
# test_later.py
import unittest
class CommonClass(unittest.TestCase):
def hello(self):
print "Hello there"
def bye(self):
print "Bye"
def seeYouAgain(self):
print "See You Again"
def whatsUp(self):
print "What's up?"
def testLater(self):
self.hello()
self.whatsUp()
if __name__ == '__main__':
unittest.main()
I reorganize in three files as follows:
# common_class.py
import unittest
class CommonClass(unittest.TestCase):
def hello(self):
print "Hello there"
def bye(self):
print "Bye"
def seeYouAgain(self):
print "See You Again"
def whatsUp(self):
print "What's up?"
# test_now.py
from common_class import *
def testNow(self)
self.hello()
self.bye()
setattr(CommonClass, 'testNow', testNow)
if __name__ == '__main__':
unittest.main()
# test_later.py
from common_class import *
def testLater(self):
self.hello()
self.whatsUp()
setattr(CommonClass, 'testLater', testLater)
if __name__ == '__main__':
unittest.main()
What are the concerns about this DRY approach?
Modifying other modules when your module is imported is a huge wart.
Instead, create subclasses:
# test_now.py
from common_class import CommonClass
class TestNow(CommonClass):
def testNow(self)
self.hello()
self.bye()
# test_later.py
from common_class import CommonClass
class TestLater(CommonClass):
def testLater(self):
self.hello()
self.whatsUp()
Or you could just move the functions out of the class since they don't depend on anything in self.
Or, once your problem grows beyond trivial examples, maybe give up on using the standard unittest framework and use something saner like py.test so you can use proper fixtures and all sorts of things.
I have a python module file (func.py) and a unit test file (f_test.py):
# func.py
def f(x):
return x + 1
x = input("Enter x: "))
print("f(x): " + str(f(x)))
and
# f_test.py
import unittest
from func import f
class MyTest(unittest.TestCase):
def test(self):
self.assertEqual(f(1), 2)
When I run f_test.py I expect the test suite to be executed (successfully).
Instead however I see the following input:
Finding files... done.
Importing test modules ... Enter x:
If I comment out the input/output lines from func.py then I get the expected behaviour.
How do I achieve it without modifying func.py?
When you import func, all the code inside is run. Running the definition def f(x)... is what creates the function f.
You can distinguish between importing and running a file by using if __name__=='__main__'.
For instance:
# func.py
def f(x):
return x + 1
if __name__ == '__main__':
x = input("Enter x: "))
print("f(x): " + str(f(x)))
When you run func.py, __name__=='__main__' will be true. When you import it, it will be false (__name__ will be 'func' instead).
Of course, the correct answer is to modify func.py. If you absolutely, positively, won't modify func.py, then you could redirect standard input:
# f_test.py
import os
import sys
import unittest
oldstdin, sys.stdin = sys.stdin, StringIO.StringIO('7')
try:
from func import f
finally:
sys.stdin = oldstdin
class MyTest(unittest.TestCase):
def test(self):
self.assertEqual(f(1), 2)
You need to add to the bottom of f_test.py:
if __name__ == '__main__':
unittest.main()
This way the tests will be executed when the file is run (I forget this more times than I would like to admit).