The UnitTest module provides components for unit testing, allowing developers to conduct self-tests.
First, the important concept in UnitTest:
test Case : The test Case object is the smallest test unit that tests the expected output for the specified input. Created by a class testcase derived class or Functiontestcase class.
Test firmware : Represents the test-related preparation and cleanup work, such as the need to create a database connection before a test is completed, and the need to close the database connection after the test. The test firmware is implemented by the setup and teardown functions that are overloaded in the TestCase subclass. Both the setup and Teardown methods are automatically executed before and after each test case execution. In addition, if the setup execution throws an exception, the test cases that are not executed are ignored and the test ends
test suite : Contains a set of test cases that are executed together. You can also include additional test suites. You can add test cases by creating objects from the Testsuite class, or you can use the Testloader provided by UnitTest to collect the specified test cases into an automatically created Testsuit object.
Test Drive : primarily responsible for performing tests and feedback test results. The Testrunner object has a run () method that receives a TestCase object or Testsuit object as a parameter that returns the result object of the test (TestResult)
Second, write the simplest test code
The following is a mathematical operation class that contains addition and division operations. and provide the corresponding unit test code, from this example, we learn some unittest basic functions:
The #exam. py file provides a sample class #coding: utf-8class operator (object) for testing: def __init__ ( SELF, A, B): self.a = a self.b = b Def add (self): return self.a + self.b def divide (self): return self.a / self.b #test. py file provides test code built with UnitTest #coding:utf-8from exam import operatorimport unittestclass Testoperator (unittest. TestCase): def setup (self): #test fixture &Nbsp; self.oper = operator (10,0) def test_add (self): #test case self.assertequal (Self.oper.add (), 10, u "Addition basic function does not meet the requirements") def test_divide (self): self.assertraises ( Zerodivisionerror, self.oper.divide ()) #def teardown ( Self): #pass if __name__ == "__main__": unittest.main (verbosity=2)
Run the test.py file to see the following output:
Test_add (__main__. Testoperator) ... oktest_divide (__main__. Testoperator) ... ok----------------------------------------------------------------------Ran 2 tests in 0.000sOK
Test classes need to inherit from TestCase
The test method is marked by default by the prefix test, so adding a non-test-prefixed helper method to the test class does not affect the collection of test cases.
The test method generally uses the Assert* method provided by TestCase to determine whether the results are in line with expectations.
Each test instance contains only one test* method, which means that the above code creates two test instances, each containing a test* method
Unittest.main provides the command line interface, initiates the test, and feeds back the test results. The parameter verbosity refers to the detailed display of the test results.
Imagine: The logic in main should be quite complex, and you need to build the test instance object? Need to find the methods that are used for testing? Need statistical test results? Wait, something we haven't met yet?
The way to solve these puzzles is straightforward, let's debug the main function, come on!
We can see that main represents a command-line interface class: We can execute the test from the command line, which is the same process as when you start the test by using main in the code.
Main = Testprogram #...class Testprogram (object): #命令行接口类 "" "A Command-li NE program, that runs a set of tests; This was primarily for making test modules conveniently executable. """
Run main (), i.e. no parameters call __init__.py to build an object.
def __init__ (self, module= ' __main__ ', defaulttest=none, argv=none, testrunner= none, testloader=loader.defaulttestloader, exit=true, verbosity=1, failfast=none, catchbreak=None, buffer=none): .... self.exit = exit self.failfast = failfast self.catchbreak = catchbreak self.verbosity = verbosity self.buffer = buffer self.defaulttest = defaulttest self.testrunner = testrunner self.testloader = testloader self.progname = os.path.basename (argv[0]) #以上是初始化工作 Self.parseargs (argv) #解析参数argv, and Load test self.runtests () #运行test, and feedback results
In the process of executing __init__.py, some initial work is done, that is, the parameters passed into main or the parameters added through the command line affect some features inside the unittest, such as the verbosity in the example represents the verbosity of the test result output, if set to 1, Or not set, the results will not show the specific testcase name, you can verify it yourself;
Next, go into Self.parseargs (argv), let's see what it does:
Def parseargs (SELF, ARGV): if len (argv) > 1 and argv[1].lower () == ' discover ': self._do_discovery (argv[2:]) return .... try: options, args = getopt.getopt (argv[1:], ' HHVQFCB ', long_opts) for opt, value in Options: if opt in ('-H ', '-h ', '--help '): self.usAgeexit () if opt in ('-Q ', '--quiet '): self.verbosity = 0 if opt in ('-V ', '-- Verbose '): #命令行参数-V represents the main parameter verbosity self.verbosity = 2 if opt in ('-f ', '--failfast '): if self.failfast is None: self.failfast = true .... #以上是从argv中读取参数, and modify the initialization values appropriately self.createtests () #创建测试实例, Return their collection-suit object (test suite) ....
First, if the parameter is ' discover ' then it goes to another branch, which is about the feature of Autodiscover, which is discussed later.
It then begins parsing argv, where the argv prefers to pass in the argv parameter of main, and if none, the sys.argv that is passed when the command line executes the script. You can see that the SYS.ARGV parameter passed by the command line and the other parameters passed to main are substituted for each other, which achieves the same effect as starting with command-line arguments and starting with the main code pass.
Next call Createtests to create the test instance, and we continue to look at:
def createtests (self): if self.testNames is None: self.test = self.testloader.loadtestsfrommodule (Self.module) else: self.test = self.testloader.loadtestsfromnames (self.testnames, self.module)
As can be seen from the name of the method, the creation of tests is loaded on the module or on the specific test method. The process of loading is primarily to collect test methods, create TestCase instances, and return the Testsuit objects containing these cases, which will be looked at in detail later.
Now, create the test instance and then go back to __init__ and execute self.runtest () to actually start the test:
Def runtests (self): if self.catchbreak: #-c means catching Ctrl + C exception during run installhandler () if self.testrunner is none: self.testrunner = runner. texttestrunner #runner默认是TextTestRunner if isinstance (self.testrunner, (type, types. ClassType)): try: testRunner = Self.testrunner (verbosity=self.verbosity, failfast=self.failfast, buffer=self.buffer) except TypeError: # didn ' t accept the verbosity, buffer or failfast arguments testrunner = self.testrunner () else: # it is assumed to be a testrunner instance testrunner = self.testrunner #以上部分是构建testRunner对象, i.e. test drive self.result = testrunner.run (self.test) # Just like the above mentioned by the runner Run method to start the test if self.exit: sys.exit (Not self.result.wassuccessful ())
As can be seen from the code, the test is initiated by the Testrunner instance through the Run function, and the default Testrunner is the Texttestrunner provided by UnitTest. This run method is designed to be very bright, and interested comrades can take a closer look, which involves the use of __call__ and __iter__ and the ingenious combination.
The simple invocation of the main function replaces the basic test function that we have done, but the interior is very complex.
Three, command line interface
As we can see above, the main and command-line interfaces are just the same class, but this class is compatible with two ways of doing it.
Use python-m unittest-h to view Help commands, where Python-m unittest discover is another branch of the command line, discussed later, it also has its own help command, which is also appended with-H
Specific commands can be researched on their own.
Iv. Test Findings
Test discovery refers to providing a starting directory that automatically searches for test cases in that directory. The same as Loadtestsfrommodule, which is provided by Testloader, is used to load the test object, returning a Testsuit object (wrapped in the searched test object). The difference is that the test discovery can be searched for a given directory.
can also be automatically discovered by the command line mentioned above: python-m unittest Discover * *
You can specify the following parameters:-S start directory (.)-T top-level directory (.) -P test File pattern matching
A brief description of the process is as follows: directory: Top level directory/start directory, which should be an import package, that is, the directory should provide the __init__.py file. In this directory. Use the-P pattern to match the file where the test case is located, and then, by default, the test method is compiled from these files to build the test instance and eventually return a suit object for the test instance collection.
Five, some useful modifiers
UnitTest supports skipping some test methods or even the entire test class, or it can be flagged that some methods are expected to not pass, so that if they do not, they will not be included in the failure count. Wait, these are all done through decorators. Let's reuse the basic examples of this article, and change the test.py to the following:
#test. py file provides test code built through UnitTest #coding:utf-8from exam import Operatorimport unittest,sysclass testoperator (unittest. TestCase): def setup (self): #test fixture self.oper = operator (10,0) @ Unittest.skip ("I trust it") # def test_add (self): #test case self.assertequal (Self.oper.add (), 10 , u "Addition basic function does not meet the requirements") @unittest. skipIf (sys.platform == ' Win32 ', "it just only run in linux! ") def test_divide (self): Self.assertraises (Zerodivisionerror, self.oper.divide ()) # Def teardown (self): #pass if __name__ == "__main__": unittest.main (verbosity=2)
After running again, the results are as follows:
Test_add (__main__. Testoperator) ... skipped ' I TRUST IT ' Test_divide (__main__. Testoperator) ... skipped ' It just only run in linux! ' ----------------------------------------------------------------------Ran 2 Tests in 0.000sOK (skipped=2)
Unittest.skipunless (condition, reason): This test will not be skipped if condition is true
Unittest.expectedfailure (): The test flag is expected to fail. Then, if the test does not meet expectations or throws an exception, the number of failures is not counted
has always admired the decorator, rather than here to appreciate the great God's demeanor, let us see exactly whether the adorner is necessary, the main application scenario is. Let's take a look at the simplest skip:
Def skip (reason): "" " unconditionally skip a test. "" " def decorator (Test_item): if not isinstance (test_item, (type, types. ClassType)): @functools. Wraps (Test_item ) def skip_wrapper (*args, ** Kwargs): raise skiptest (reason) test_item = skip_wrapper test_item.__unittest_skip__ = true test_item.__unittest_skip_why__ = reason return test_item return decorator
As you can see, if the skip adorner modifies the test class, add the __unittest_skip__ property directly, which is judged in the instance run. If you modify a test method, the decorated method is replaced with a method that triggers the skiptest exception, and the __unittest_skip__ property is also added to the decorated method.
The added properties are used when the test instance runs, and are judged in the Run method provided by the TestCase class:
if (GetAttr (self.__class__, "__unittest_skip__", false) or getattr (testmethod, "__unittest_skip__", False)): # if the class or method was skipped. try : skip_why = (GetAttr (self.__class__, ' __unittest_skip_why__ ', ") or getattr (testmethod, ' __unittest_skip_why__ ', ")) self._addskip (Result, skip_why) finally: result.stoptest (self) return
If the test method or the class to which it belongs exists the __unittest_skip__ property is true, the test is skipped. As we can see above, the instance runs only to check the value of the __unittest_skip__ property and does not fetch the skiptest exception, so why is the skip adorner going to replace the modified function?
Can't think of, comment out if block, the program can still run good, leave a doubt it!
This article is from the "Nameless" blog, please be sure to keep this source http://xdzw608.blog.51cto.com/4812210/1612063
Python unittest Framework