Transfer from:
JUnit design pattern analysis
IT Pioneer senior adviser grid liu
by the grid liu article published in the The consulting and training.
Abstract
JUnit is an excellent Java unit testing framework, by two world-class software guru Erich Gamma and Kent Beck co-developed. This paper will introduce the reader to the process in the development of JUnit What is the application of design patterns.
Keywords: unit testing JUnit design pattern
1 JUnit Overview
1.1 JUnit Overview
JUnit is an open source java testing framework, it is Xuint test architecture an implementation. JUnit unit testing framework in the design, set three overall objectives, the first is to simplify the writing test, including testing framework that simplifies the study and the preparation of the actual test unit; the second is to test unit to maintain persistent; the third is to use existing tests to the preparation of related tests. so these purposes are the root cause of why the use of models.
1.2 JUnit developers
JUnit originally developed by Erich Gamma and developed by Kent Beck. Erich Gamma, Ph.D., is the Swiss software Suez International Centre for object-oriented technology, technical director, is also a masterpiece, development of the schema definition, CRC cards in the use of the software development process, HotDraw software architecture, based on the xUnit testing framework, re-evaluate the software development process in the test-first programming model. is summed up mode to share with you. I follow the issues raised, mode selection, the specific implementation, the use of results show how to model this process applied to JUnit.
2 JUnit architecture
JUnit's design to Patterns Generate Architectures (see Patterns Generate Architectures, Kent Beck and Ralph Johnson, ECOOP 94) ways to structure the system. The design idea is to apply design patterns from scratch, and then one by one, until finally you get the right system architecture.
3 JUnit design pattern
3.1 JUnit test framework components
l target of testing methods and collection process, which can be referred to as test cases. (TestCase)
l of test cases can be accommodate multiple test cases (TestCase), to test the package is called. (TestSuite)
l description of the test results and records. (TestResult)
l during the test event listener (TestListener)
l of each test method is inconsistent with the expected happened description of the situation, saying that the test failed element. (TestFailure)
l JUnit Framework error in the exception. (AssertionFailedError)
3.2 Command (command) mode
3.2.1
we must first understand the problem, JUnit is a test framework, test the ability to develop test cases. and then test the composition of these requests (sometimes may be one or more), sent to the JUnit FrameWork, then JUnit perform the test, the final report details the test results. These include the implementation of the time, wrong way, wrong location. This test case developers do not need to know the details of the internal JUnit, as long as the request consistent with its definition format. from JUnit's point of view, it does not need to know the information requested TestCase operation, only use it as a command to execute, then execute the test results sent to testers. This allows TestCase JUnit framework and open to independent developers to make do not know that the receiving party to request more information on the requesting party, not to know how to be received, and how to be executed to achieve the system's loose coupling.
3.2.2 Mode Selection
Command (command) mode ( See Gamma, E., et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995) was able to better meet the demand. quote intent (intent), encapsulates a request into an object, so you can use different parameters of the request of the customers of; on the request queue or log requests ... Command told us an operation can generate an object and give it an execute (execution) method. In order to achieve
3.2.3 Command mode, first define an interface Test, which is the Run Command's Execute method. and then use the Default Adapter model provides a default implementation of this interface, abstract class TestCase, so that Our developers can integrate the default implementation of this without having to achieve from the Test interface.
We first analyze the Test interface. It is countTestCases there is a method, which to count the number of the test TestCase, other One way is to Command mode Excecute our way, here named run. There is also a parameter TestResult, the results of statistical tests it
public interface Test {
/ **
* Counts the number of test cases that will be run by this test.
* /
public abstract int countTestCases ();
/ **
* Runs a test and collects its result in a TestResult instance.
* /
public abstract void run (TestResult result);
}
TestCase is abstract implementation of this interface, it adds the name of a test, because when it is created for each TestCase should have a name, so if a test fails, you can identify which test failed.
public abstract class TestCase extends Assert implements Test {
/ **
* the name of the test case
* /
private String fName;
public void run (TestResult result) {
result.run (this);
}
}
so that our developers, writing test cases, simply inherited TestCase, to complete the run method can then get JUnit test cases, execute its run method, the test results recorded in the TestResult, at present time being so you can understand.
3.2.4 after effects
to consider the following Command mode using the structure of the system brings those results:
l Command mode will enable the requesting party (TestCase development) and call the party (JUnit Fromwork) were decoupled
l Command mode is the new TestCase easy to join, without changing the existing class, TestCase class can only inherit, so convenient for testers
l Command mode can be combined into a number of TestCase compound command, actually you will see that it TestSuit complex commands, of course, it uses the Composite pattern
l Command mode request TestCase easy to combine the request queue, so that the party receiving the request (Junit Fromwork), easy to decide whether execution of the request, or if that fails the test case or errors can be reported immediately to stop
l Command mode in case of need to facilitate the implementation of the requested Undo and Redo, and records Log, this part of the current JUnit is not implemented in the future, it is easy to join
3.3 Composite (combination)
3.3.1 problem
system state in order to gain the confidence, you need to run multiple test cases. Through our use of Command mode, JUnit can easily run a single test case after the produce test results. But in the actual testing process, several test cases need to be combined into a composite test case, as a request sent to JUnit. JUnit to the face such a problem must take into account the type of test requests, TestCase is a single or a composite of TestCase, and even in the end to distinguish between the number of TestCase. Junit framework that will complete the code like the following:
if (isSingleTestCase (objectRequest)) {
/ / if is a single TestCase, the implementation of run, get the test results
objectRequest.run ()
} else if (isCompositeTestCase (objectRequest)) {
/ / If it is a composite TestCase, will perform different operations, complex algorithm and then sub-
/ / solution, after running each TestCase, the last access to test results, while also considering
/ / If the error between the test how ????,< br> hhhhhhhhhh < br> hhhhhhhhhh
}
JUnit must consider the distinction between such a request (TestCase) type (a single or composite testCase testCase), but in fact in most cases, the tester that the two are the same. For this The difference between the use of, and causes the program to become more complex, difficult to maintain and extend. so to consider how the design can not only distinguish between JUnit TestCase or a single composite TestCase, to unify them into the same request?
3.3.2 Mode Selection
when the test call does not have to care about the running of one or more test cases in the request, can easily solve this problem pattern is Composite (combined) mode. quote its intentions, the object composition into tree structures to represent part - the whole hierarchy. Composite allows users on a single object and the combinations are consistent with the object. In this part - the whole hierarchy is the key solution, can be seen as part of a single TestCase , and the composite TestCase (TestSuit) as a whole. so that we can use this mode may solve the problem right.
Composite pattern structure
Composite mode is introduced into the following participants:
n Component: This is an abstract role, which the object provided to the participants a combination of the interface. this role, gives a total behavior of the interface and the default was. In fact, to our Test interface that defines the run method.
n Composite: to achieve a total of Interface and maintain a test set. is our composite TestCase, TestSuit
n Leaf: combination of representatives of the object, it does not lower the child object, only to participate in the definition of a combination of the behavior of the original object, in fact, a single test case TestCase it only implements the Test interface methods.
fact componsite mode interface implemented according divided into two forms, called safe-and Transparent. JUnit-style use of the security structure, so that there is no management TestCase sub-object method.
3.3.3 achieve
composite model tells us to introduce an abstract class Component for Leaf objects and composite objects defined public interface. The basic intent of this class is to define an interface. In Java, Use Composite mode, priority is given to use interface, rather than an abstract class, so the introduction of a Test interface. of course, our leaf is a TestCase. The source code is as follows:
/ / composite model of the role of
public interface Component Test {
public abstract void run (TestResult result);
}
/ / composite model of the Leaf role
public abstract class TestCase extends Assert implements Test {
public void run (TestResult result ) {
result.run (this);
}
}
, list Composite source. named the TestSuit class. TestSuit has a property fTests (Vector type) in the preserved The sub-test case (child test), provide methods to achieve increased addTest child objects TestCase, and also provides testCount and tests and other methods to manipulate child objects. Finally, run () method implementation to delegate to its child objects (delegate), the final addTestSuite method implementation also provides a recursive, constructed tree.
public class TestSuite implements Test {
private Vector fTests = new Vector (10);
public void addTest (Test test) {
fTests. addElement (test);
}
public int testCount () {
return fTests.size ();
}
public Enumeration tests () {
return fTests.elements () ;
}
public void run (TestResult result) {
for (Enumeration e = tests (); e.hasMoreElements ();) {
Test test = (Test) e.nextElement () ;
runTest (test, result);
}
}
public void addTestSuite (Class testClass) {
addTest (new TestSuite (testClass));
}
} Analysis of the Composite pattern
after we set out to achieve its composition, as shown below:
Note that all of the above Test interface code is implemented. As consistent with TestCase and Test TestSuit both interfaces, we can addTestSuite then recursively combined into the TestSuite TestSuite, this would constitute a tree structure. all developers can create their own TestSuit. testers can create a combination of these packages TestSuit to run them all the TestCase.
public class AllTests extends TestCase {
public AllTests (String s) {
super (s);
}
public static Test suite () {
TestSuite suite1 = new TestSuite (my test TestSuit1 );
TestSuite suite2 = new TestSuite (my test TestSuit2);
suite1.addTestSuite (untitled6.Testmath.class);
suite2.addTestSuite (untitled6.Testmulti.class);
suite1. addTest (suite2);
return suite1;
}
}
its structure as shown
3.3.4 we consider the effect of
Composite mode through the use of the structure of the system to bring those results:
l simplifies the code JUnit JUnit TestSuite composite structures can be centrally and a single object TestCase. JUnit development becomes simple and easy to make, because it does not distinguish the difference between part and whole, do not write full if else in the select statement.
l defines the class TestCase objects and TestSuite TestCase hierarchy of basic objects can be combined into more complex composite object TestSuite, and these combined objects and can be combined, such as our last example, this constant to recurse indefinitely. client code, the basic object of any place used to facilitate the use of composite object, greatly simplifying system maintenance and development.
l makes it easier to add new types of TestCase, such as the very model described below Decorate Extended functionality
l TestCase make the design more general.
3.4 Template Method (template method)
3.4.1
in the actual test questions in order to test the business logic, you must construct some parameters or some resources before it can be tested, and finally to release the system resources. If the test database application, you must create a database connection Connection, and then perform database operations, and finally to achieve the release of the database connection. the following code:
public void testUpdate () {
/ / Load the Oracle JDBC driver
DriverManager.registerDriver (new oracle.jdbc.OracleDriver ());< br> String url = jdbc: oracle: thin: @ localhost: 1521: ORA91 ;
/ / Connect to the database
Connection conn = DriverManager.getConnection (url, hr, hr);
PreparedStatement pstmt =
conn.prepareStatement (insert into PersonTab values (?));< br> / / Bind the Person object
pstmt.setObject (1, person, OracleTypes.JAVA_STRUCT);
/ / Execute the insertion
pstmt.executeUpdate ()
/ / Disconnect
conn . close ();
}
In fact, many such cases, such as test EJB, the need for JNDI, LookUp, get Home interface, the other the case of initialization parameters. But if we have a TestCase several test methods, such as testing of the database Insert, Update, Delete, Select and other operations, these operations have to be in every way to obtain a database connection in the connection, and then test the business logic, and finally release the connection. This increases the development staff repeatedly writing the code, the original design goals with inconsistent JUnit? how to solve this problem?
3.4.2 Mode Selection
the next problem to be solved is to give developers a convenient place initialization code, and finally run the source code released, and the results of each test will not affect other test results. so to reuse the code provides developer efficiency.
Template Method (Template Method ) better to involve us in issues. quote its intentions, certain steps of the algorithm. will remain the same, regardless of the initialization code, how to write, or how to write test code.
Template Method (template method) static structure as shown below:
design to the two roles here, has the following responsibilities
l AbstractClass more abstract definition of operations in order to allow sub-classes. and to achieve a specific template method, which gives a top-level logical framework, and the composition of the logical steps in the corresponding abstract operation, deferred to the sub-class implementation . template method may also call some specific methods.
l ConcreteClass to achieve the abstract parent class method of operation, they are integral steps in the template method. Each may have multiple ConcreteClass AbstractClass corresponding, respectively, each achieving a ConcreteClass abstract operations, which makes top-logic implementation vary.
3.4.3 so we first of all to achieve
TestCase into several methods, which is to allow developers to abstract operations to achieve, which is the specific template methods Now we see TestCase source
public abstract class TestCase extends Assert implements Test {
/ / abstract operations, to allow subclasses to achieve
protected void setUp () throws Exception {
}
/ / abstract operations, to allow subclasses to achieve
protected void runTest () throws Throwable {
}
/ / abstract operations, to allow subclasses to achieve
protected void tearDown () throws Exception {< br>}
/ / specific template method, the definition of a logical framework
public void runBare () throws Throwable {
setUp ();
try {
runTest ();
}
finally {
tearDown ();
}
}
}
setUp methods defined as protected so that developers realized, to initialize the test information, such as database connection JNDI lookup EJB Home interface and other information, the tearDown method is to achieve the release of resources after the completion of the test and other cleanup. RunTest method is the test developers to implement business logic. Finally, the method runBare TestCase is a template method, which implements the logic of the test frame, the composition of the test logic steps setUp, runTest, teardown, deferred to a specific sub-class implementation, such as a specific test class
public class TestHelloWorldTestClientJUnit1 extends TestCase {
public void setUp () throws Exception {
super.setUp ();
initialize ();
create ();
}
public void testGetMessage () throws RemoteException {
String strMsg = Hello World;
assertNotNull (ERROR_NULL_REMOTE, helloWorld);
this.assertEquals (strMsg, helloWorld.getMessage ());< br>}
public void tearDown () throws Exception {
helloWorldHome = null;
helloWorld = null;
super.tearDown ();
}
}
research to see their class diagram:
3.4.4
us consider the effect of using the Template Method pattern through the post to system architecture brings those effects:
l in each test case of public behavior (initialization information and release resources, etc.) are extracted to avoid code duplication and simplify the developer's work. < br> l in the TestCase in the same part of the realization of an algorithm, and the behavior of the variable sub-class to leave. enhance the flexibility of the system. the JUnit framework is only responsible for the outline and skeleton algorithm, the test developers given the algorithm is responsible for all the logical steps.
3.5 Adapter (adapter)
3.5.1 problem
Command mode we have applied to the performance of a test. Command as dependent on a single execute () This method (in the TestCase called run ()) to its call. This simple interface allows us to call through the same interface to different implementations of a command.
If you implement a test case to be implementation inheritance Testcase, and then implement the run method, the actual (testRun), however, so we put all the test cases are different ways to achieve the same class, such a result will cause a large amount of sub-categories, so that testing of the system maintenance very difficult, and only the setUp and tearDown testRun services, other tests must also complete the code, thus increasing the workload of the developer, how to solve this problem?
in order to avoid the rapid spread of class, imagine a given test use case class (testcase class) can be achieved in many different ways, each method has a descriptive name, such as testMoneyEquals or testMoneyAdd. This test case does not meet the simple command interface. it is also brought another problem is that All test methods from the perspective of the caller testing (JUnit framework) point of view is the same. how to solve this problem?
3.5.2 Mode Selection
think the applicability of design patterns, Adapter (adapter) mode will greet the mind. Adapter has the following intent The Command (TestCase's run) to facilitate the implementation of the framework test. Adap ...
No comments:
Post a Comment