Test concept
For a long time, the software developers I have been in contact have rarely test work in the process of development. Most of the projects are written in test documents when final acceptance. Some items do not even test documents. Now there is a change. We have been advocating UML, RUP, software engineering, CMM, with only one, improve the quality of software preparation. Extremely an extreme example: If you are a super programming designer, a legendary figure. (You can drink coffee while listening to music while writing the module about the process schedule in this operating system, and it is completed in two days!) I really admit that there is such a person. (The guy who writes the VI editor in UNIX is this kind of person.) However, it is very unfortunate that these fans have not left how to cultivate a fruit. So these mortals - can only focus on several points at the same time (according to scientific statistics, I don't believe that the general people can only consider up to 7 or so, the master can reach 12) And not only overview of the global understanding of details - can only expect other ways to ensure the quality of the software we have written.
To explain how our mortals are stupid. There is a smart person proposing the concept of software entropy: starting from a very good state of the design, as the new features continue to join, the program gradually lost the original structure, eventually become a group Mess. You may argue, in this example, the design is very good. It is actually not good. If it is good, you will not happen. Yes, it seems that you become smart, but you should also pay attention to two questions: 1) We can't expect the structure of the Dinosaur Era (probably ten years ago) to design it now. 2) Customers who have signed rights can not ignore whether a new feature will have any effect on the structure of the software, even if an impact is also a problem that programmer needs to consider. If you refuse to join this new feature you think is deadly, then you are likely to lose your housing loans and bread (for Chinese engineers, maybe it is rice or noodles, you must see you is a South or a northerner).
In addition, it is necessary to explain that some of the books I have seen have not written so people (I am sorry ...). I hope to see the brothers and sisters of this article can easily accept the concept of tests and put them into implementation. So some places have some exaggerations, welcome brothers and sisters who have an in-depth understanding of tests, they can enlighten me.
Ok, we are now going to be correct. To test, you must understand the purpose of the test. I think the purpose of the test is simple and attractive: writing high quality software and solving software entropy. Imagine, if you write the software and Richard Stallman (the head of the GNU, FSF), is there a sense of accomplishment? If you consistently maintain this high level, I guarantee that your salary will change.
Test also classified, white box test, black box test, unit test, integrated test, function test .... We have never, how to classify, how to classify. First, those who are useful for us, about other tests, interested people can refer to other information. White box test refers to the tests made under the conditions that the software knows how to complete the function and what to do (what). It is generally done by the developer. Because developers understand the software you have written. This article is also based on white box test. The black box test refers to the tests made under the conditions that the software that is tested is tested. It is generally done by testers. Black box test is not our focus. This article is mainly concentrated on unit testing, and the unit test is a white box test. The purpose is to verify that one or a number of categories are working properly as designed. Integrated testing is to verify that all classes can cooperate with each other, collaborate to complete specific tasks, and we are currently not concerned about it. The test I mentioned below unless specifically described, it is generally a unit test. It is important to emphasize that the test is a continuous process. That is to say, during the entire process of testing and development, the unit test is particularly suitable for the development process of iterative incremental type (ITERATIVE AND INCREMENTAL). Martin Fowler (a bit like a reference to the word) or even think: "You should not write the program before you don't know how to test the code. Once you have completed the program, the test code should also be completed. Unless the test is successful, you can't think You have written procedures that you can work. "I don't expect that all developers can have such a high sense, this level is not one. But once we understand the purpose and benefits of the test, we will naturally insist on introducing tests during the development process.
Because we are testing novices, we also ignore those complex test principles, first say the simplest: test is whether the expected result is consistent with the actual results. If the consistency passes, it will fail. Look at the example below:
// The class that will be tested
Public class car {
Public int getWheels () {
Return 4;
}
}
// Class to execute the test
Public class testcar {
Public static void main (String [] args) {
Testcar mytest = new testcar ();
Mytest.TestGetWheels ();
}
Public TestGetWheels () {
INT EXPECTEDWHEELS = 4;
Car mycar = car ();
IF (expectedwheels == mycar.getwheels ())
System.out.println ("TEST [CAR]: GetWheels Works Perfect!");
Else
System.out.println ("TEST [CAR]: GetWheels Doesn't Work!");
}
}
If you have written the code immediately, you will find two questions, first, if you want to perform the test class TestCar, you must have to be handled into the following command:
[Windows] D:> Java TestCar
[UNIX]% Java TestCar
Even if the test is as simple as exemplified, you may not want to type the above command at each test, and you want to click on an integrated environment (IDE) to execute the test. The following chapters will be introduced to these issues. Second, if there is no certain specification, the writing of the test class will become another standard that needs to be defined. No one wants to see how others design test class. If everyone has different design test classes, light maintenance is tested enough, who is still maintaining test class? There is another point I don't want to mention, but this problem is too obvious, the test class is more than the class being tested! Does this mean this double work? Do not! 1) No matter how complex the getWheels method of the test class-Car, the test class-TestCar's TestGetWheels will only maintain the same amount of code. 2) Improve the quality of the software and solve the problem of software entropy is not cost. TestCar is the price. What we can do now is to minimize the consideration of the payment: The test code we have written must be readily read by maintenance personnel. We have a certain specification to write test code. It is best to support these specifications. Ok, what you need is JUnit. An Open Source project. In the words of its homepage, "Junit is a regression test framework written by Erich Gamma and Kent Beck. It is used for Java developers to write unit tests." The so-called frame is Erich Gamma and Kent Beck. Take some of the box boxes, you must follow this bar box: inherit a class and implement an interface. In fact, it is the specification we mentioned earlier. Fortunately, JUnit is currently recognized by most software engineers. We will get a lot of support to follow JUnit. The regression test is to test the code written: write some, test some, debug some, then loop this process, you will continue to repeat the previous test, even if you are writing other classes, due to software entropy Existence, you may find that a certain operation of the fifth class will cause failure of the second class when you write a fifth class. We grab this big bug by regression test.
Return test frame - JUnit
Through the previous introduction, we have a probably contour for Junit. I know what it is doing. Let us now rewrite the test class TestCar to make it conform to the JUnit specification - can run in JUnit.
// Execute the test class (JUnit version)
Import junit.framework. *;
Public Class Testcar Extends Testcase {
Protected int expectedwheels;
protected car mycar;
Public testcar (String name) {
Super (Name);
}
protected void setup () {
EXPECTEDWHEELS = 4;
Mycar = new car ();
}
Public static test suite () {
/ *
* The Type Safe Way
*
Testsuite suite = new testsuite ();
Suite.addtest
New testcar ("car.getwheels") {
Protected void runtest () {testGetWheels ();
}
);
Return suite;
* /
/ *
* The Dynamic Way
* /
Return New Testsuite (Testcar.class);
Public void testGetwheels () {
Assertequals (expectedwheels, mycar.getwheels ());
}
}
The TestCar after the revision has faced unrecognizable. Let us first understand what these changes have mean, and then see how this test is implemented.
1> Import statement, introduce the class of JUnit. (is that OK)
2> Inherited Testcase. A TestCase can be treated as a collection of methods for testing a class. For details, please refer to JUnit Information
3> Setup () Sets the task of initialization. We will see SETUP will have special use.
4> TestGetWheeeels () compares the value of the expected value and mycar.getwheels () and prints the result of the comparison. Assertequals is the method defined in junit.framework.assert, and junit.framework.testcase inherits JUnit.Framework.Assert.
5> Suite () is a very special static method. JUnit's Testrunner calls the Suite method to determine how many tests can be executed. The above example shows two methods: static method is to construct an internal class and use the constructor to name the test (Test Name, such as Car.Getwheels), which covers the runtest () method, indicating that the test needs to be executed. Those methods - TestGetWheels (). The dynamic approach is to use the internal province to implement RUNTEST () to find out those tests that need to be implemented. At this time, the name of the test is the name of the test method (Test Method, such as TestGetWheels). JUnit will automatically find and call this class's test method.
6> Watch TestSuite as a container for parcel test. If the test is tested as a leaf node, TestSuite is a branch node. In fact, Testcase, TestSuite, and TestSuite make up a Composite Pattern. One of JUnit's documentation has a special explanation how to use the Pattern constructed JUnit framework. Interested friends can view JUnit information.
How to run this test? Handmade method is to type the following command:
[Windows] D:> java junit.textui.teestrunner testcar
[UNIX]% junit.textui.teestrunner testcar
Don't worry about the character of you want to knock, in the future, as long as you have a few mice. The result of the operation should be as follows, indicating a test and passed the test:
.
Time: 0
OK (1 tests)
If we modify the value returned in Car.GetWheels () to 3, simulate an error, you will get the following results:
.F
Time: 0
There WAS 1 Failure:
1) TestGetWheels (testcar) junit.framework.assertionFaileDEctionError: Expected: <4> But Was: <3>
At testcar.testgetwheels (testcar.java: 37)
Failures !!!
Tests Run: 1, Failures: 1, Errors: 0
Note: Dices on Time represent the number of tests, if the test is passed, OK is displayed. Otherwise, the rear side of the dot is marked, indicating that the test failed. Note that in an emulated error test, we will get a detailed test report "Expected: <4> But Was: <3>", which is enough to tell us where the problem happens. Here is the process of debugging, testing, debugging, testing, until you get the expected result. Design by Contract (I can't translate this sentence)
Design by Contract This is a design technology developed by Bertrand Meyer (founder of the Eiffel language). I found that using Design by Contract in JUnit will bring unexpected effects. The core of Design by Contract is an assession. As an assertion is a Boolean sentence, the statement cannot be false. If it is a fake, it indicates a bug. Design by Contract uses three assertions: pre-conditions, post-conditions and invariant (Invariants), not intended to discuss details of Design by Contract, but hope to in the test Can play its role.
The front condition can be used to determine if the test is allowed to enter the test before performing the test. Such as Expectedwheels> 0, MyCar! = NULL. The rear condition is used to determine whether the test results are correct after the test execution. Such as expectedwheels == mycar.getWheels (). The invariance is particularly useful in judging the consistentness of the transaction. I hope that Junit can enhance the Design by Contract as an enhancement of future versions.
Refactoring (this sentence I still can't translate)
Refactoring has not been directly connected with the test, but is related to software entropy, but since we say that we say that test can solve software entropy problems, we must say a solution. (Only the software entropy can only be found, and Refactoring can solve the problem of software entropy.) Software entropy brings a problem: Do you need to redesign the structure of the entire software? In theory, this should be, but reality is not allowed to do this. This is or due to the reason, or due to the reason. The structure of redesigning the entire software will bring us short-term pain. Keeping a patch to make the software to make us long-term pain. (No matter what, we always be in hot and hot)
Refactoring is a term for describing a technology that we can avoid short-term pain brought about by reconstructing the entire software. When you refactor, you don't change the functionality of the program, but change the structure inside the program so that it is easier to understand and use. Such as: The name of the method will move a member variable from a class to another, and the two similar methods are abstracted into the parent class. Every step made is small, but the 1-2 hours of Refactoring work makes your program structure more suitable for the current situation. Refactoring has some rules:
1> Do not refactor existing code while adding new features. There must be a clear boundaries between the two. If you add a new feature to the rest of 1-2 hours a day.
2> Before you start Refactoring, and to ensure that the test can pass smoothly after the refactoring. Otherwise, Refactoring has no meaning. 3> Small refactoring, big is not refactoring. If you intend to refactor the entire software, there is no need to refactoring.
Refactoring is only necessary to add new features and debug bugs. Don't wait until the last level of the delivery software is refactoring. That is not the difference between the patch. Refactoring can also display its power in the regression test. To understand, I don't oppose the patch, but I have to remember that the patch is the supreme trick that should be used. (Patching also requires high technology, please refer to Microsoft website)
IDE supports JUnit
Currently support Junit's Java IDE includes IDE mode personal evaluation (1-5, full 5)
Forte for Java 3.0 Enterprise Edition Plug-in 3
JBuilder 6 Enterprise Edition Integrated with IDE 4
Visual Age for Java Support N / A
How to use JUnit in IDE is a very specific thing. Different IDEs have different ways of use. Once you understand the nature of JUnit, it is easy to use. So we don't rely on specific IDE, but concentrate on how to use JUnit to write unit test code. Anxious people can see the information.
Introduction to JUnit
Since we have had a general understanding of Junit, I hope to provide you with a slightly official manual to write JUnit test documents, understand some of the key terms and concepts. But what I want to declare is that this is not a full manual, I can only think that it is a new entry manual. Software with other OpenSource has the same problem, JUnit's documentation has the rules, concise and completeness of the commercial software document. The document written by developers is always not clear, and the whole document needs to refer to the "Official" guide, API manual, email discussion group email, even in the source code and related comments.
In fact, the problem is not so complicated unless you have very special requirements, otherwise, you can get the required information.
installation
First you have to get JUnit's packages, download the latest package from Junit (as of this writing this article, the latest version of JUnit is 3.7). Uninpack it in an appropriate directory. This is in the installation directory (that is, you have selected the directory you choose). You find a file named JUnit.jar. Add this JAR file to your ClassPath system variable. (Ide's settings will be different, see the Configuration Guide for your favorite IDE) JUnit is installed. Too easy!
Once you have installed JUnit, you may want to try our Car and TestCar classes, no problem, I have already run, the result you get should be similar to the results I listed. (To prevent new JUnit to make my article outdated)
Next, you may write test code first, write work code, or opposite, write work code, and then write test code. I am more likely to use the former method: write test code first, write work code. Because this makes it clearly understand the behavior of work classes when writing work code.
Pay attention to writing a certain test code (such as the example in the article) does not make sense, only test code can help us find bugs, test code has its value. In addition, the test code should also test the work code. If the parameters called by the method are sent to the null value, the error value, and the correct value, see if the behavior of the method is as expected. You now know the basic steps for writing test classes:
1> Expand TestCase class;
2> Override Runtest () method (optional);
3> Write some TestXxxxxx () methods;
FixTure
The problem that the solution is that if you want to execute multiple tests for one or a number of classes, what should I do? JUnit has special solutions to this.
If you need to perform multiple tests in one or a number of classes, these classes have become the test of the Context. In JUnit, it is called FixTure (like the Mycar and Expectedwheels in the TestCar class). When you write a test code, you will find that you spend a lot of time configuration / initialization related tests of FixTure. Putting the code for configuring fixture is not available in the construction of the test class, because we ask for multiple tests, I don't want the result of a test accident (if this is your request, then ) The results affecting other tests. Usually several tests use the same fixture, and each test has its own places that you need to change.
To do this, Junit provides two ways to define in the TestCase class.
protected void setup () throws java.lang.exception
Protected void teardown () throws java.lang.exception
Overwrite Setup () methods, initialize all tests of FixTure (you can even create a network connection in Setup), which is slightly different from each test in the Testxxxx () method.
Cover Teardown () (I always think of a guitar song called rain drops), release your permanent resources allocated in Setup (), such as database connections.
When the JUnit performs a test, it calls setup () before performing each Testxxxxxx () method, and calls the Teardown () method after performing each Testxxxxxx () method, thereby ensuring that the test does not affect each other.
Testcase
Need to remind, define considerable Assert methods in the JUnit.Framework.assert class, mainly assert (), assert (), assertequals (), assertnull (), assertsame (), asserttrue (), asserttrue (), fail (), etc. method. If you need to compare your own defined classes, such as Car. The Assert method requires you to override the Equals () method of the Object class to compare the difference between the two objects. Practice shows: If you override the equals () method of the Object class, it is best to override the HashCode () method of the Object class. Further, the toString () method of the Object class is also covered. This makes the test result more readable.
When you set fixture, the next step is to write the desired TestXxxx () method. Be sure to guarantee the PUBLIC property of the TestXxxx () method, otherwise the test cannot be called via the REFLECTION.
Each extension TestCase class (that is, you have written test class) has multiple TestXxxx () methods. A TestXxx () method is a test. To run this test, you must define how to run the test. If you have multiple TestXxxx () methods, you have to define multiple times. JUnit supports two methods of running a single test: static and dynamic methods. The static method is to cover the RUNTEST () method of the TestCase class, which is generally created by the internal class:
Testcase Test01 = New Testcar ("Test getWheels") {
Public void runtest () {
TestGetwheels ();
}
}
It is necessary to pay attention to each test with a static method (this name can be arbitrarily, but you must hope that this name has a meaning), so you can distinguish that test failed.
The dynamic method is to implement RUNTEST () in the order to create a test instance. This requires the name of the test method that needs to be called:
Testcase Test01 = New Testcar ("TestGetWheels");
JUnit will dynamically find and call the specified test method. The dynamic method is very concise, but if you type the wrong name, you will get a strange NosuchMethodeXception exception. Dynamic methods and static methods are very good, you can choose according to your preferences. (Don't worry first, there is a more cool way to wait for you.)
Testsuite
Once you have created some test instances, the next step is to let them run together. We must define a TestSuite. In JUnit, this requires you to define a static suite () method in the TestCase class. The suite () method is like the Main () method, JUnit uses it to perform tests. In the suite () method, you add a test instance to a TestSuite object and return to this TestSuite object. A TestSuite object can run a set of tests. TestSuite and TestCase have implemented Test Interfaces, while the Test interface defines the methods required to run tests. This allows you to create a TestSuite with a combination of TestCase and TestSuite. That's why we say TestCase, TestSuite, and TestSuite have made composite pattern. The example is as follows:
Public static test suite () {
Testsuite suite = new testsuite ();
Suite.Addtest (New Testcar ("TestGetWheels");
Suite.Addtest (New Testcar ("TestgetSeats");
Return suite;
}
Starting with JUnit 2.0, there is a simpler method for dynamically defining test instances. You only need to pass classes to TestSuite, and Junit automatically creates a corresponding test instance based on the test method name. So your test method is preferably named Testxxx (). The example is as follows:
Public static test suite () {
Return New TestSuite (TestCar.class);
}
From JUnit's design, we can see that JUnit is not only available for unit testing, but also for integration testing. For integration tests with JUnit, please refer to the relevant information.
For compatibility, the following examples are listed in the following example:
Public static test suite () {
Testsuite Suite = new testsuite (); suite.addtest (SUITE.ADDTEST
New testcar ("getWheels") {
Protected void runtest () {testGetWheels ();
}
);
Suite.addtest
New testcar ("getseats") {
Protected void runtest () {testgetseats ();
}
);
Return suite;
}
Testrunner
With TestSuite, we can run these tests, Junit provides three interfaces to run test
[Text Ui] junit.textui.teStrunner
[AWT UI] junit.awtui.teStrunner
[Swing ui] junit.swingui.teStrunner
We have already seen the text interface in front, let's take a look at the graphical interface:
The interface is simple, type the class name -TestCar. Or when you start the UI, type the class name:
[Windows] D:> java junit.swingui.teestrunner testcar
[UNIX]% junit.swingui.teestrunner testcar
From the graphics UI, you can run the test to check the test results. There is also a problem. Need to note: If JUnit reports the test is not successful, Junit will distinguish between failures and errors. Failure is a result that is expected by the ASSERT method. The error is caused by an unexpected problem, such as ArrayIndexOutofboundSexception.
Since Testrunner is very simple, the interface is also intuitive, so there is not much introduction. Friends can refer to the relevant information.
JUnit Best Practice
Martin Fowler said: "When you try to print some information or debug an expression, write some test code to replace those traditional methods." At the beginning, you will find that you always want you Create some new fixture, and test seem to make your programming speed slow down. Soon, you will find that you reuse the same fixture, and new tests usually only involve adding a new test method.
You may write a lot of test code, but you will soon find that the test you want to think is really useful. The test you need is the test that will fail, that is, those that you think will not fail, or you think that you should fail.
We mentioned earlier testing is a process that does not interrupt. Once you have a test, you have to keep it working properly to verify the new work code you join. Don't run test every few days or in the end, you should run test code every day. This kind of investment is small, but you can make sure you get a reliable work code. Your rework rate is low, you will have more time to write work code.
Don't think that the pressure is large, don't write the test code. On the contrary, write test code will gradually alleviate your pressure, and you should have an exact understanding of the behavior of the class by writing test code. You will write an efficient work code faster. Here are some specific methods or better practices for writing test code:
1. Do not initialize FixTure with the constructor of TestCase, and use the setup () and Teardown () methods.
2. Do not rely or assume the order of test run, because JUnit uses the Vector Save Test Method. So different platforms will take out test methods from the vector in different orders.
3. Avoid writing TestCase with side effects. For example: If the subsequent test depends on some specific transaction data, it is not necessary to submit transaction data. Simple will be able to roll. 4. When inheriting a test class, remember to call the SETUP () and Teardown () method of the parent class.
5. Place the test code and work code together and compile and update synchronously. (Task, which is supported in Ant, support JUnit.)
6. Test class and test methods should have a consistent naming scheme. To form a test class name as before the work class is preceded.
7. Make sure the test is not related to time, do not rely on the use of expired data for testing. It is difficult to reproduce the test during subsequent maintenance.
8. If you write software to the international market, you should consider international factors when preparing tests. Don't test only with the mother tongue Locale.
9. Use JUnit to provide an ASSERT / FAIL method as much as possible, and the code can be more concise.
10. Test should be as small as possible and perform fast.
In fact, JUnit can also be used to integrate testing, but I didn't involve it. There are two reasons: First, because there is no unit test, integration test is not talking. We are very difficult for us to accept the concept of testing, and if you introduce an integrated test, it will be more difficult. Second, I am more lazy, I hope to give the task of the integrated test to the test personnel. There are some related articles on JUnit's website, you can turn over.
JUnit and J2EE
If you carefully think about it, you will find that JUnit has its own limitations, such as testing of graphics interfaces, and testing for servlet / JSP, and EJBs. In fact, JUnit has a way to test for the GUI interface, servlet / jsp, javabean, and EJB. About the test of GUI is more complicated, suitable for introducing a whole article. Not much here.
The test we did actually had an implicit environment, and the JVM our class needs this JVM to execute. In the J2EE framework, servlet / JSP, EJB requires its own operating environment: Web Container and EJB Container. Therefore, if you want to test the servlet / jsp, EJB, you need to deploy it in the corresponding Container to test. Since EJB does not involve UI issues (unless the EJB operation XML data, the test code at this time is more difficult to write, it is possible that you can compare whether the two DOM trees contain the same content) As long as the test code can be run, you can run the test code. At this point, the setup () method is particularly useful, and you can use JNDI to find specific EJBs in the Setup () method. In the TestXxx () method, the method of tested and tested these EJBs is called.
The JavaBean referred to here also does not have a problem with the UI, for example, we use JavaBean to access the database, or use JavaBean to package EJB. If such JavaBean does not use the service provided by Container, you can test directly, just like the general class of the general class mentioned earlier. If such JavaBean uses the service provided by Container, you need to deploy it in the Container to test. The method is similar to EJB.
Testing for servlet / JSP is more difficult, it is recommended to construct httpRequest and httpresponse in the test code, and then compare, which requires developers to have a relatively deep understanding of the HTTP protocol and the internal implementation of the servlet / JSP. I think this is not realistic. Some people also propose using httpunit. Since I know much about CACTUS and HTTPUNIT, I can't make a suitable suggestion. I hope that the prophets can enlighten me.