I am new to unit testing using python and am designing a simple unit test setup. This is what I have made:
/python_test
__init__.py
unittest_main.py
test_abc/
__init__.py
test_abc.py
test_pqr/
__init__.py
test_pqr.py
All the __init__.py files are empty and the other files have some basic content:
unittest_main.py
import os
import sys
import glob
import inspect
import unittest
from sets import Set
if len(sys.argv) > 1:
testCases = glob.glob('test_*')
testTargets = Set([])
for testTarget in sys.argv:
if testTarget == "test_all":
testTargets = testCases
break
else:
for testCase in testCases:
if testCase == testTarget:
testTargets.add(testCase)
break
if len(testTargets) > 0:
print testTargets
suiteList = []
for testTarget in testTargets:
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],testTarget)))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
suiteList.append(unittest.defaultTestLoader.loadTestsFromModule("python_test"+"."+testTarget+"."+testTarget))
mainSuite = unittest.TestSuite(suiteList)
unittest.TextTestRunner(verbosity=1).run(mainSuite)
else:
"No Proper Test Targets Specified"
else:
print "No Test Targets Specified"
test_abc.py
import unittest
class test_abc(unittest.TestCase):
def setUp(self):
print "Setup Complete"
def tearDown(self):
print "Tear Down Complete"
def test_abc_main(self):
self.assertEqual(1, 2, "test-abc Failure")
if __name__ == "__main__":
unittest.main()
test_pqr.py
import unittest
class test_pqr(unittest.TestCase):
def setUp(self):
print "Setup Complete"
def tearDown(self):
print "Tear Down Complete"
def test_abc_main(self):
self.assertEqual(1, 2, "test-pqr Failure")
if __name__ == "__main__":
unittest.main()
Question:
I am able to test the test cases separately but when I use the parent directly file to run all the tests, nothing happens?
$ ~/src/python_test$ python unittest_main.py test_all
['test_pqr', 'test_abc']
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
The installed Python Version is 2.7.3
Because you're passing module names to the loader, you want to use loadTestsFromName instead of loadTestsFromModule.
Related
I would like to ensure with tests that:
- the application cannot be imported
- the application can be started as a real application (i.e: python src.py)
I'm interested about that, why the following is not working:
src.py
class A:
def x(self):
print('this is x')
if __name__ == '__main__':
A().x()
test.py (snippet)
class Test(unittest.TestCase):
#mock.patch('src.A.x')
def test_main(self, mock_x):
import src
mock_x.assert_any_call()
This test fails... why?
Because the name of the module when imported is src, not __main__.
The easiest solution would be to move that code into a function:
def main():
A().x()
if __name__ == '__main__':
main()
and in your test, you would invoke src.main()
#mock.patch('src.A.x')
def test_main(self, mock_x):
import src
src.main()
mock_x.assert_any_call()
To test that a module is not importable you do not need to use mocks.
See assertRaises.
Just check if an error is thrown on import od module.
with self.assertRaises(...):
...
I know it is a bit silly question, but using links provides below, I am still unable to create testsuite.
I have now two test cases (there will be much more), let assume that the name of there are:
class step1(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
def test_case1(self):
[...]
if __name__ == "__main__":
unittest.main()
and:
class step2(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
def test_case2(self):
[...]
if __name__ == "__main__":
unittest.main()
I want to create other file .py file: testsuite, which can aggregate test_case1, test_case2, test_case3...
I tried something like that, for example:
import unittest
import step1
import step2
def suite():
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.step1(test_case1))
test_suite.addTest(unittest.step2(test_case2))
if __name__ == "__main__":
result = unittest.TextTestRunner(verbosity=2).run(suite())
sys.exit(not result.wasSuccessful())
Error: AttributeError: 'module' object has no attribute 'step1'
You can use addTest() and pass TestCase instance to it, you also miss return statement:
def suite():
test_suite = unittest.TestSuite()
test_suite.addTest(step1())
test_suite.addTest(step2())
return test_suite
or, in one line using addTests():
test_suite.addTests([step1(), step2()])
I want to write tests for my main file,calc.py, with unittest in module file, MyTests.py.
Here is my main python file, calc.py:
import myTests
def first(x):
return x**2
def second(x):
return x**3
def main():
one = first(5)
two = second(5)
if __name__ == "__main__":
main()
try:
myTests.unittest.main()
except SystemExit:
pass
And here is my MyTests.py file:
import unittest
import calc
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.testInput = 10
def test_first(self):
output = calc.first(self.testInput)
correct = 100
assert(output == correct)
def test_second(self):
output = calc.second(self.testInput)
correct = 1000
assert(output == correct)
When i run my calc.py, i get the following output:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Why does unittest prints me that "Ran 0 test"?
And what is the right way to write unittest in module?
unittests.main() looks for TestCase instances in the current module. Your module has no such testcases; it only has a myTests global.
Best practice is to run the tests themselves. Add the __main__ section there to the myTests.py file:
import unittest
import calc
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.testInput = 10
def test_first(self):
output = calc.first(self.testInput)
correct = 100
assert(output == correct)
def test_second(self):
output = calc.second(self.testInput)
correct = 1000
assert(output == correct)
if __name__ == '__main__':
unittest.main()
and run python myTests.py instead.
Alternatively, pass in the imported myTests module into unittest.main(). You may want to move the import myTests line down into __main__, because you have a circular import too. That is fine in your case, myTests doesn't use any globals from calc outside of the test cases, but it is better to be explicit about this.
if __name__ == "__main__":
main()
try:
import myTests
myTests.unittest.main(myTests)
except SystemExit:
pass
I want to have the files of my application under the folder /Files, whereas the test units in /UnitTests, so that I have clearly separated app and test.
To be able to use the same module routes as the mainApp.py, I have created a testController.py in the root folder.
mainApp.py
testController.py
Files
|__init__.py
|Controllers
| blabla.py
| ...
UnitTests
|__init__.py
|test_something.py
So if in test_something.py I want to test one function that is in /Files/Controllers/blabla.py, I try the following:
import unittest
import Files.Controllers.blabla as blabla
class TestMyUnit(unittest.TestCase):
def test_stupid(self):
self.assertTrue(blabla.some_function())
if __name__ == '__main__':
unittest.main()
And then from the file testController.py, I execute the following code:
import TestUnits.test_something as my_test
my_test.unittest.main()
Which outputs no failures, but no tests executed
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
[Finished in 0.3s]
I have tried with a test that has no dependences, and if executed as "main" works, but when called from outside, outputs the same:
import unittest
def tested_unit():
return True
class TestMyUnit(unittest.TestCase):
def test_stupid(self):
self.assertTrue(tested_unit())
if __name__ == '__main__':
unittest.main()
Question: how do I get this to work?
The method unittest.main() looks at all the unittest.TestCase classes present in the context.
So you just need to import your test classes in your testController.py file and call unittest.main() in the context of this file.
So your file testController.py should simply look like this :
import unittest
from UnitTests.test_something import *
unittest.main()
In test_something.py, do this:
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestMyUnit, 'test'))
return suite
In testController.py, do this:
from TestUnits import test_something
def suite():
suite = unittest.TestSuite()
suite.addTest(test_something.suite())
return suite
if __name__ == '__main__':
unittest.main(defaultTest='suite')
There is a workaround of using subprocess.call() to run tests, like:
import subprocess
args = ["python", "test_something.py"]
subprocess.call(args)
i have dozen of test cases in different folders. In the root directory there is a test runner.
unittest\
package1\
test1.py
test2.py
package2\
test3.py
test4.py
testrunner.py
Currently I added the four test cases manually into a test suite
import unittest
from package1.test1 import Test1
from package1.test2 import Test2
from package2.test3 import Test3
from package2.test4 import Test4
suite = unittest.TestSuite()
suite.addTests(unittest.makeSuite(Test1))
suite.addTests(unittest.makeSuite(Test2))
suite.addTests(unittest.makeSuite(Test3))
suite.addTests(unittest.makeSuite(Test4))
result = unittest.TextTestRunner(verbosity=2).run(suite)
if not result.wasSuccessful():
sys.exit(1)
How to let test runner test all test cases automatically? Such as:
for testCase in findTestCases():
suite.addTests(testCase)
In my opinion you should switch to unittest2 or other test frameworks with discovery features. Discovery tests is a really sane way to run them.
Most known are:
nosetests
py.test
For example with nosetest is sufficient to run nosetests from the project root directory and it will discover and run all the unit tests it find. Pretty simple.
Notice also that unittest2 will be included in python 2.7 (and backported till 2.4 I guess).
The above modules are good but NoseTests can be funny when trying to enter in parameters and this is also faster and fit's into another module nicely.
import os, unittest
class Tests():
def suite(self): #Function stores all the modules to be tested
modules_to_test = []
test_dir = os.listdir('.')
for test in test_dir:
if test.startswith('test') and test.endswith('.py'):
modules_to_test.append(test.rstrip('.py'))
alltests = unittest.TestSuite()
for module in map(__import__, modules_to_test):
module.testvars = ["variables you want to pass through"]
alltests.addTest(unittest.findTestCases(module))
return alltests
if __name__ == '__main__':
MyTests = Tests()
unittest.main(defaultTest='MyTests.suite')
If you want to add results to a log file add this at the end instead:
if __name__ == '__main__':
MyTests = Tests()
log_file = 'log_file.txt'
f = open(log_file, "w")
runner = unittest.TextTestRunner(f)
unittest.main(defaultTest='MyTests.suite', testRunner=runner)
Also at the bottom of the modules you are testing place code like this:
class SomeTestSuite(unittest.TestSuite):
# Tests to be tested by test suite
def makeRemoveAudioSource():
suite = unittest.TestSuite()
suite.AddTest(TestSomething("TestSomeClass"))
return suite
def suite():
return unittest.makeSuite(TestSomething)
if __name__ == '__main__':
unittest.main()
What I did is a wrapper script which running separate test files:
Main wrapper run_tests.py:
#!/usr/bin/env python3
# Usage: ./run_tests.py -h http://example.com/ tests/**/*.py
import sys, unittest, argparse, inspect, logging
if __name__ == '__main__':
# Parse arguments.
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", help="increase output verbosity" )
parser.add_argument("-d", "--debug", action="store_true", dest="debug", help="show debug messages" )
parser.add_argument("-h", "--host", action="store", dest="host", help="Destination host" )
parser.add_argument('files', nargs='*')
args = parser.parse_args()
# Load files from the arguments.
for filename in args.files:
exec(open(filename).read())
# See: http://codereview.stackexchange.com/q/88655/15346
def make_suite(tc_class):
testloader = unittest.TestLoader()
testnames = testloader.getTestCaseNames(tc_class)
suite = unittest.TestSuite()
for name in testnames:
suite.addTest(tc_class(name, cargs=args))
return suite
# Add all tests.
alltests = unittest.TestSuite()
for name, obj in inspect.getmembers(sys.modules[__name__]):
if inspect.isclass(obj) and name.startswith("FooTest") and len(name) > len("FooTest"):
alltests.addTest(make_suite(obj))
# Run tests.
result = unittest.TextTestRunner(verbosity=2).run(alltests)
sys.exit(not result.wasSuccessful())
Then another wrapper for tests:
class FooTest(unittest.TestCase):
def __init__(self, *args, cargs=None, **kwargs):
super().__init__(*args, **kwargs)
self.vdisplay = Xvfb(width=1280, height=720)
self.vdisplay.start()
self.args=cargs
self.log=logging
def setUp(self):
self.site = webdriver.Firefox()
def kill(self):
self.vdisplay.stop()
Then each test in separate files would look like:
import sys, os, unittest
from FooTest import FooTest
class FooTest1(FooTest):
def test_homepage(self):
self.site.get(self.base_url + "/")
log.debug("Home page loaded.")
Then you can easily run tests from shell like:
$ ./run_tests.py -h http://example.com/ test1.py test2.py
You can use wildcard to specify all files within certain directories, or use a new globbing option (**) to run all tests recursively (enable by shopt -s globstar).