I was trying to learn unit testing in Python, specifically the unittest
module.
Consider the following lines:
import unittest class abc(unittest.TestCase): def xyz(): ... if __name__ == "__main__": unittest.main()
I could see all my test cases running because of the call to unittest.main()
.
I was just curious to know how this call is making all the test cases run.
I know since I'm inheriting from unittest.TestCase
for every test class, it is doing all the magic. Any insights?
Python unittest module is used to test a unit of source code. Suppose, you need to test your project. You know what kind of data the function will return. After writing huge code, you need to check it whether the output is correct or not.
The unittest module provides a rich set of tools for constructing and running tests. This section demonstrates that a small subset of the tools suffice to meet the needs of most users. A testcase is created by subclassing unittest.TestCase .
Unit testing allows the programmer to refactor code or upgrade system libraries at a later date, and make sure the module still works correctly (e.g., in regression testing). The procedure is to write test cases for all functions and methods so that whenever a change causes a fault, it can be quickly identified.
main associated with unittest is actually an instance of TestProgram which, when instantiated, runs all your tests.
Below is the relevant code copied from the unittest source at http://pythonhosted.org/gchecky/unittest-pysrc.html:
735 class TestProgram: 752 - def __init__(self, module='__main__', defaultTest=None, 753 argv=None, testRunner=None, testLoader=defaultTestLoader): 754 if type(module) == type(''): 755 self.module = __import__(module) 756 for part in module.split('.')[1:]: 757 self.module = getattr(self.module, part) 758 else: 759 self.module = module 760 if argv is None: 761 argv = sys.argv 762 self.verbosity = 1 763 self.defaultTest = defaultTest 764 self.testRunner = testRunner 765 self.testLoader = testLoader 766 self.progName = os.path.basename(argv[0]) 767 self.parseArgs(argv) 768 self.runTests() 769 770 - def usageExit(self, msg=None): 771 if msg: print msg 772 print self.USAGE % self.__dict__ 773 sys.exit(2) 774 775 - def parseArgs(self, argv): 776 import getopt 777 try: 778 options, args = getopt.getopt(argv[1:], 'hHvq', 779 ['help','verbose','quiet']) 780 for opt, value in options: 781 if opt in ('-h','-H','--help'): 782 self.usageExit() 783 if opt in ('-q','--quiet'): 784 self.verbosity = 0 785 if opt in ('-v','--verbose'): 786 self.verbosity = 2 787 if len(args) == 0 and self.defaultTest is None: 788 self.test = self.testLoader.loadTestsFromModule(self.module) 789 return 790 if len(args) > 0: 791 self.testNames = args 792 else: 793 self.testNames = (self.defaultTest,) 794 self.createTests() 795 except getopt.error, msg: 796 self.usageExit(msg) 797 798 - def createTests(self): 799 self.test = self.testLoader.loadTestsFromNames(self.testNames, 800 self.module) 801 802 - def runTests(self): 803 if self.testRunner is None: 804 self.testRunner = TextTestRunner(verbosity=self.verbosity) 805 result = self.testRunner.run(self.test) 806 sys.exit(not result.wasSuccessful()) 807 808 main = TestProgram
So when you execute unittest.main()
, an object of TestProgram
gets created which calls self.runTests()
at line 768. The constructor also takes your current file as the default module containing the tests ( module='__main__'
).
When runTests()
is called, it in turn calls self.testrunner.run()
. When you refer to the "run" method of TextTestRunner
class, you will find that it actually runs and reports all your test results.
Test discovery is done by TestProgram.parseArgs
at line 775 when you call unittest.main(). self.createTests
at line 798 is actually responsible for discovering all your test cases and creating a test suite. This is all the magic.
Internally, unittest.main()
is using a few tricks to figure out the name of the module (source file) that contains the call to main()
.
It then imports this modules, examines it, gets a list of all classes and functions which could be tests (according the configuration) and then creates a test case for each of them.
When the list is ready, it executes each test in turn.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With