First, software testing
The development of large software system is a very complicated process, in which the error caused by human factors is very much, so the software must have corresponding quality assurance activities in the development process, and software testing is the key measure to ensure the quality. As described in software entropy (software entropy): A program starts with a good design, and as new functions are constantly added, the program gradually loses its original structure and eventually becomes a mess (in fact, the original "good state" has to add a question mark). The purpose of the test is very simple and attractive, which is to write high quality software and solve the problem of software entropy.
Unfortunately, software developers are rarely able to test software in the process of coding, and most software projects are tested only during final acceptance, and some projects do not even have a test plan at all! With the enhancement of software quality consciousness, many software development organizations are turning to software engineering methods such as UML, CMM, RUP, XP, and so on, in order to improve the quality and make the software development process more controllable, these methods are very strict to the test. So that the test in the software development process to begin to truly reflect the role.
As a system engineering, software testing involves all aspects of the whole software development process and requires the joint efforts of managers, designers, developers and testers. As the main force in the software development process, today's programmers, in addition to writing implementation code, but also bear the task of unit testing, and therefore must adopt a new mode of work:
- Write and maintain a set of detailed unit test cases;
- Construct unit test and acceptance test cases first, then write code;
- Write code based on the constructed test case.
The unit test is responsible for verifying the smallest software Design Unit (module), which uses the description of the module as a guide in the Software design documentation to test the important program branches to discover errors in the module. Because the software module is not a separate program, in order to carry out unit testing must also write a large number of additional code, thereby virtually increasing the workload of developers, the current solution to this problem is a better way to use the test framework. The test framework is the key to unit testing with the XP method, this is especially true when you need to construct a large number of test cases, because if you build and execute these tests entirely by hand, it will become a time-consuming and tedious task, and the test framework can solve them well.
Developers using the Python language can use the Pyunit written by Steve Purcell as a unit test framework, and by fusing unit tests into the Pyunit test framework, Python programmers can add, manage, and execute test cases more easily, And the test results are analyzed. In addition, automated unit testing (regression testing) can also be implemented using Pyunit.
Second, standardize the Python unit test
Testing is a continuous process throughout the development process, in a sense, the process of software development is actually the testing process. As Martin Fowler said, "You shouldn't write a program until you know how to test the code." And once you have completed the program, the test code should also be completed. Unless the test succeeds, you can't assume that you have written a program that works. "
The basic principle of testing is to compare the expected result to the actual execution result, if the test succeeds, or the test fails. To better understand the pyunit of this automated test framework, let's look at a simple example, assuming we're going to test the widget class in Example 1:
Example 1. widget.py
# class Widget that will be tested:
def __init__ (self, size = ()):
self._size = size
def getsize (self):
return self._size
def resize (self, width, height):
if width 0 or height < 0:
raise valueerror, illegal siz E "
self._size = (width, height)
def dispose (self):
Pass
A python programmer who makes unit tests by hand is likely to write a test code similar to the example 2,
Example 2. manual.py
From widget Import Widget
# classes class
Testwidget:
def testsize (self):
expectedsize = (40, 40); C18/>widget = Widget ()
if widget.getsize () = = Expectedsize:
print "Test [widget]: GetSize works perfected!"
Else:
print "Test [Widget]: GetSize doesn ' t work!"
# test
If __name__ = = ' __main__ ':
myTest = Testwidget ()
mytest.testsize ()
A little attention. It's not hard to find that this manual testing method has many problems. First of all, the test program is written without a certain standard to follow, 10 programmers are completely likely to write 10 different test programs, and if every Python programmer has its own way of designing test classes, it will be a hassle to keep the classes tested, and who will be able to maintain the test class. Second, you need to write a lot of secondary code to do unit testing, and in Example 1 the code used to test is even more than the code being tested, which will undoubtedly increase the workload of the Python programmer.
In order for the unit test code to be more easily understood by the test and maintenance staff, the best solution would be for the developer to follow certain specifications to write the code for the test, and the Python programmer would use the Pyunit automated test framework to construct the unit test case. Currently Pyunit has been recognized by most Python developers as a de facto unit test standard. If you use Pyunit to do the same test, the test code will look like Example 3:
Example 3. auto.py
From widget Import widget
import unittest
# class Widgettestcase (UnitTest) that performs tests
. TestCase):
def setUp (self):
self.widget = Widget ()
def teardown (self):
self.widget = None
def Testsize (self):
self.assertequal (Self.widget.getSize (), ())
# construct test set
def suite ():
suite = UnitTest. TestSuite ()
suite.addtest (Widgettestcase ("Testsize")) return
Suite
# test
if __name__ = ' __main_ _ ":
unittest.main (defaulttest = ' suite ')
After using the Unit test framework for Pyunit, the code for testing has been modified accordingly:
- The import statement is used to introduce the UnitTest module.
- To allow all classes that perform tests to inherit from the TestCase class, you can consider TestCase as a collection of methods that test a particular class.
- The setup () and teardown () are the methods defined in the TestCase class when the initialization of the test is performed in the Teardown () method, and after the test cleanup is done in the () method.
- The Assertequal () method is called in testsize () to compare the return and expected values of the GetSize () method in the Widget class to ensure that the two are equal, and that assertequal () is also a method defined in the TestCase class.
- Provides a global method named Suite () that pyunit the suit () method to determine how many test cases need to be executed during the execution of the test, and can consider testsuite as a container containing all test cases.
Although it may seem a bit complicated, pyunit makes it possible for all Python programmers to use the same unit test method, the test process is no longer messy, but the orderly behavior under the guidance of the same specification, which is the most benefit of the automated unit Testing framework using Pyunit.
Three, automatic test frame Pyunit
After a general understanding of software testing theory and Pyunit, here is a concrete example of how Python programmers can use Pyunit for unit testing. All the code is debugged under Python 2.2.2, and the operating system uses red Hat Linux 9.
3.1 Installation
The Pyunit module is needed for unit testing in Python, and Python 2.1 and later versions have Pyunit as a standard module, but if you're using older versions of Python, you'll have to install it yourself. The Pyunit website (http://sourceforge.net/projects/pyunit) can be downloaded to the Pyunit's latest source package, where pyunit-1.4.1.tar.gz is used.
After downloading the Pyunit package, perform the following command to decompress it:
[Root@gary source]# tar xzvf pyunit-1.4.1.tar.gz
To use the Pyunit module in a python program, The easiest way to do this is to make sure that the files unittest.py and unittestgui.py in the Pyuni package are included in the Python search path, either by setting the PYTHONPATH environment variables directly or by executing the following commands to copy them to the current search in Python Diameter medium:
[Root@gary source]# cd pyunit-1.4.1
[root@gary pyunit-1.4.1]# python setup.py install
3.2 Test Case TestCase
The most basic component of software testing is the test case, Pyunit uses the TestCase class to represent the test case, and requires that all classes used to perform the tests must inherit from the class. The test code implemented by the TestCase subclass should be self-contained (self contained), which means that the test case can run either individually or together with other test cases.
TestCase in the Pyunit test framework as a running entity for test cells, Python programmers can derive custom test processes and methods (test cells) from the command and composite design patterns. Multiple testcase can also be combined into a test case collection. Pyunit test Framework When you run a test case, the setup (), Runtest (), and teardown () methods defined by the TestCase subclass are executed sequentially, and the simplest test cases simply overwrite the Runtest () method to execute the specific test code. As shown in Example 4:
Example 4. static_single.py
Import UnitTest
# performs the test class
Widgettestcase (unittest. TestCase):
def runtest (self):
widget = widget ()
self.assertequal (Widget.getsize (), (40, 40))
To construct an instance of the above Widgettestcase class in the Pyunit test framework, you should call its constructor without any arguments:
TestCase = Widgettestcase ()
A test case typically tests only one method in a software module, using the overlay Runtest () method to construct test cases called static methods in Pyunit, if you want to test multiple methods in the same software module, you typically need to construct multiple classes that perform tests, as shown in Example 5:
Example 5. static_multi.py
Import UnitTest
# Tests the test Case
class Widgetsizetestcase (unittest) of the GetSize () method. TestCase):
def runtest (self):
widget = widget ()
self.assertequal (Widget.getsize (), ())
# Test Case
Class Widgetresizetestcase (unittest) for testing the Resize () method. TestCase):
def runtest (self):
widget = widget ()
widget.resize ()
self.assertequal ( Widget.getsize (), (100, 100))
With a static approach, the Python programmer has to write a test class for each method to test (the class executes the test by overwriting the Runtest () method) and generates an object to be tested in each test class. When writing test cases for the same software module, many times the object has the same initial state, so the Python programmer using the method has to do the same initialization for the object being tested in each test class, which is often a time-consuming and tedious task.
A better solution is to use the dynamic approach provided by Pyunit, write only one test class to complete the test of the entire software module so that the initialization of the object can be done in the Setup method, and the release of the resource can be done in the teardown () method, as shown in Example 6:
Example 6. dynamic.py
Import UnitTest
# performs the test class
Widgettestcase (unittest. TestCase):
def setUp (self):
self.widget = Widget ()
def teardown (self):
self.widget.dispose ()
Self.widget = None
def testsize (self):
self.assertequal (Self.widget.getSize (), ())
def testresize (self):
self.widget.resize (self.assertequal) (
self.widget.getSize (), (100, 100))
The biggest benefit of adopting a dynamic approach is that the test class is well structured, and all the code used to test a software module can be implemented in the same class. Instead of overwriting the Runtest () method, the dynamic method writes multiple test methods for the test class (usually the method begins with test), and the name of the test method must be given when creating an instance of the TestCase subclass. To specify which method in the test class should be invoked when running the test case for the Pyunit test framework:
Sizetestcase = Widgettestcase ("testsize")
resizetestcase = Widgettestcase ("Testresize")
3.3 Test Case Set Testsuite
With a complete unit test that rarely executes only one test case, developers often need to write multiple test cases to perform a complete test of a software function, which is referred to as a test case set and is represented in the Pyunit by the Testsuite class.
After creating instances of testcase subclasses as test cases, the next thing to do is to organize them with testsuit classes. The Pyunit test framework allows the Python programmer to define a global function named Suite () in the unit test code as a portal to the entire unit test, Pyunit to complete the test process by calling it.
Def suite ():
suite = UnitTest. TestSuite ()
suite.addtest (Widgettestcase ("Testsize"))
Suite.addtest (Widgettestcase ("Testresize"
)) Return Suite
You can also define a testsuite subclass directly and complete the addition of all test cases in its initialization method (__INIT__):
Class Widgettestsuite (UnitTest. TestSuite):
def __init__ (self):
unittest. Testsuite.__init__ (self, map (widgettestcase,
("Testsize",
"Testresize"))
This requires only one instance of the class to be returned in the Suite () method:
Def Suite (): Return
widgettestsuite ()
If all of the test methods in the class used for testing are tested, the Python programmer can even construct a testsuite using the Makesuite () method provided by the Pyunit module:
Def Suite (): Return
unittest.makesuite (widgettestcase, "test")
In the Pyunit test framework, the Testsuite class can be viewed as a container for the TestCase class, which is used to organize multiple test cases so that multiple test cases can be automatically completed in one test. In fact, Testsuite can contain testsuite in addition to TestCase, which can form a larger set of test cases:
Suite1 = mysuite1. Thetestsuite ()
suite2 = Mysuite2. Thetestsuite ()
alltests = UnitTest. TestSuite ((suite1, Suite2))
3.4 Implementation Testing
The ultimate goal of writing test cases (TestCase) and organizing them into test case sets (TestSuite) is only one: Implement the Test and get the final result. Pyunit uses the Testrunner class as the basic execution environment for test cases to drive the entire unit test process. Instead of using the Testrunner class directly in unit tests, Python developers use their subclass Texttestrunner to complete the test and display the test results in textual form:
Runner = UnitTest. Texttestrunner ()
Runner.run (Suite)
Examples of using Testrunner to implement tests are shown in Example 7,
Example 7. text_runner.py
From widget Import widget
import unittest
# class Widgettestcase (UnitTest) that performs tests
. TestCase):
def setUp (self):
self.widget = Widget ()
def teardown (self):
self.widget.dispose ()
Self.widget = None
def testsize (self):
self.assertequal (Self.widget.getSize (), ())
def Testresize (self):
self.widget.resize (MB)
self.assertequal (Self.widget.getSize (), (100, 100))
# test
if __name__ = = "__main__":
# Construct test set
suite = UnitTest. TestSuite ()
suite.addtest (Widgettestcase ("Testsize"))
Suite.addtest (Widgettestcase ("Testresize"))
# Execute Test
runner = UnitTest. Texttestrunner ()
Runner.run (Suite)
To perform this unit test, you can use the following command:
[Xiaowp@gary code]$ python text_runner.py
The results of the run should look like this, indicating that 2 test cases were executed and both passed the test:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
If you modify the data to simulate an error situation, you will get the following result:
. F
==========================================
fail:testresize (__main__. widgettestcase)
----------------------------------------------------------------------
traceback (most Recent call last):
File "text_runner.py", line, in Testresize
self.assertequal (Self.widget.getSize (), ( )
File "/usr/lib/python2.2/unittest.py", line 286, failunlessequal
raise Self.failureexception, \
assertionerror: (MB)!=
-------------------------------------------------------------- --------
Ran 2 tests in 0.001s
FAILED (Failures=1)
By default, Texttestrunner outputs the results to sys.stderr, but if a file object is passed to the constructor when the Texttestrunner class instance is created, the output is redirected to the file. Using the Texttestrunner class is a good choice when driving unit tests in a python interactive environment.
The Pyunit module defines a global method called Main, which makes it easy to turn a unit test module into a test script that can run directly, and the main () method uses the Testloader class to search all the test methods contained in the module and automatically executes them. If a python programmer can name all the test methods according to the Convention (beginning with test), just add the following lines to the end of the test module:
if __name__ = = "__main__":
Unittest.main ()
Examples of using the main () method to implement tests are shown in Example 8,
Example 8. main_runner.py
From widget Import widget
import unittest
# class Widgettestcase (UnitTest) that performs tests
. TestCase):
def setUp (self):
self.widget = Widget ()
def teardown (self):
self.widget.dispose ()
Self.widget = None
def testsize (self):
self.assertequal (Self.widget.getSize (), ())
def Testresize (self):
self.widget.resize (MB)
self.assertequal (Self.widget.getSize (), (100, 100))
# test
if __name__ = = "__main__":
Unittest.main ()
To perform this unit test, you can use the following command:
[Xiaowp@gary code]$ python main_runner.py
All test methods in the test class Widgettestcase will be executed automatically, but if you only want to execute the Testsize () method, you can use the following command:
[xiaowp@gary code]$ python main_runner.py widgettestcase.testsize
If you define Testsuite in a unit test script, you can also specify the test set to run. Use the-h parameter to view all the parameters that might be used to run the script:
[Xiaowp@gary code]$ python main_runner.py-h
To make the unit test more affinity, the Pyunit package also provides a graphical interface test script unittestgui.py, after copying it to the current directory, you can execute the following command to launch the test tool for Main_ To test all test cases in the runner.py script:
[xiaowp@gary code]$ python unittestgui.py main_runner
The interface for the test tool to move rows is shown in Figure 1:
Figure 1. Graphics testing Tools
Click the Start button to start executing all test cases, and the test results will look like the following in Figure 2:
Figure 2 Test results
The graphical interface allows for better unit testing, and it is also more convenient to query test results. Pyunit for tests that fail to pass, indicating whether it is a failure (failure) or error, which is the expected result of an Assert class method (such as assertequal), and the error is caused by an unexpected condition.
Iv. Summary
Testing is the key to ensure the quality of software, the new software development methods require programmers to write code before writing test cases, and in the software development process of continuous unit testing, so as to minimize the production of defects (bugs). Software Unit Testing is the cornerstone of the XP approach, and the test framework provides a uniform specification for programmer unit Testing, and Python programmers can use Pyunit as a framework for automated unit testing in the software development process.