This article is in the study summary, welcome reprint but please specify Source: http://blog.csdn.net/pistolove/article/details/42167015
As a programmer, I do not know whether you are in the development process or even do not write test procedures, probably most people think this is normal, in fact, from a personal point of view is also very normal, because after all, testers are specially tested! But if you can take a serious look at where the programmer is going to spend the most time, you'll find that writing code is actually a very small part. Some times are used to decide what to do next, others to spend on design, and most of the time to debug. I'm sure every reader remembers spending countless hours on commissioning, countless times a night. Every programmer can tell a story that spends a day (or more) just to find a small problem. Fixing errors is usually quick, but finding a mistake is a nightmare. When you fix a mistake, there will always be another error, and it must be a long time before you notice it. Then you have to take the time to find it. It seems that we really need to build a test system.
(Ⅰ) value of self-test code
In fact, if you look carefully at where the programmer is going to spend the most time, you will find that writing code is actually a very small part of it. Some times are used to decide what to do next, others to spend on design, and most of the time to debug. I'm sure every reader remembers spending countless hours on commissioning, countless times a night. Every programmer can tell a story that spends a day (or more) just to find a small problem. Fixing errors is usually quick, but finding a mistake is a nightmare. When you fix a mistake, there will always be another error, and it must be a long time before you notice it. Then you have to take the time to find it.
Therefore, we should take a long-term perspective to look at the problem, and not for a moment of convenience caused many inconvenience later. As we often say to ourselves, "Well, it looks like it's working, and then we're going to put the developed code on the other side and develop something else." Do you really work properly? Did you see it working properly? Have you tested it yet? We often deceive ourselves, and may have to deceive ourselves, because developers have so much time! However, we should build a test system to find out the problems. However, writing good test program code can greatly improve my programming speed, even without refactoring.
I think each class should have a test function and test itself with this class. In general, we do incremental development, so it's a good idea to add tests for each class to ensure correctness at the end of each increment. Just as the previous project was relatively small, it was a fairly simple test to perform at about the weekly increments, but it would be cumbersome to do so because each test would output the results to the console, and it would have to be checked individually. Later, realize that it is not necessary to stare at the screen to check whether the information is correct, big can let the computer help to do. This ensures that all tests are fully automated, allowing them to examine their own test results.
A set of tests is a powerful bug detective that can drastically reduce the time it takes to find bugs. Of course, it's not that easy for us to persuade others to do the same. Writing a test program means writing a lot of extra code. Unless you experience the speed with which this method is programmed, self-testing does not show its meaning. Many people have never learned how to write test programs or even consider tests at all, which is bad for writing self-test code. If you need to run tests manually, it's depressing, and nobody wants to stare at the screen all the time, but it's really interesting to write the test code if you can run it automatically.
Typically, we think that the test code should be written after the program has been developed. In fact, the most useful time to write test code is before you start programming. Whenever you need to add an attribute, write the appropriate test code first. It sounds like a deviant. Because writing test code is actually asking yourself: what does it take to add this feature? Writing test Code also allows you to focus on interfaces rather than implementations, which is always good. Pre-written test code also provides a clear end sign for your work: Once the test code is working properly, the work can be finished.
The sermon sounds always right, although I believe everyone can benefit from writing self-test, but this is not the focus of this article. This paper focuses on refactoring, and refactoring requires testing. If you want to refactor, you have to write test code. In Java, the usual test technique is testing main, meaning that each class should have a main () for testing. This is a reasonable habit, but may not be easy to manipulate, which is actually difficult to run multiple tests easily. It's a good idea to build a standalone class for testing and run it in one framework to make testing easier.
(Ⅱ) junit test FrameworkThis article uses the JUnit test framework, which is very simple, but allows you to do all the important things you need to test. The following describes the development of test code for some IO classes.
Note: (a) the TestCase (test fixture) inherits the asset class and implements the Test interface. (b) TestSuite (test suite) implements the Test interface.
(1) First create a Filereadertester class to test the file reader. Any class that contains test code (that is, a test case) must inherit the TestCase class provided by the test framework. Where Test is a suite of tests that can contain test cases and other test suites.
public class Filereadertester extends TestCase {public Filereadertester (String method) {super (method);}}
(2) Second, this new class needs to have a constructor. After completion we can add test code, preferred to set test fixture, that is, the test object sample. Because of the need to read a file, prepare a test file as follows:
Bradman 99.95 6789 334 29Pollock 60.95 4 2256 334 4Marry 80.95 4 2256 334 29Jone 77.65 4 6789 334* 11Sutcliffe 63.25 8 6789 331934 18
(3) Before further application of this document, we should prepare the test fixture. TestCase provides two functions: SetUp () is used to produce related objects, TearDown () is responsible for deleting them now that we have the appropriate test fixture, we can begin to write the test code. First Test read (), read some characters, and then check that subsequent read characters are correct.
public void Testread throws ioexception{char ch = ' & ', for (int i = 0; i < 4; i++) { ch = (char) _input.read (); C30/>assert (' d ' = = ch)}}
assert () plays the automatic test role. If the argument value of assert () is true, everything is fine, otherwise we will receive an error notification. Here's how to run the test process:
(1) The first step is to produce a test suite. Design a suite () for this, as follows:
public static Test Suite () {TestSuite suite = new TestSuite (); Suite.addtest (New Filereadertester ("Testread")); Suite.addtest (New Filereadertester ("Testreadandend")); return suite;}
This test suite contains only one test case object, the Filereadertester instance. When creating a test case object, the name of the function being tested is passed to the constructor as a string, creating an object to test the specified function, which is associated with the object through the Java reflection mechanism.
(2) also need a separate Testrunner class, Testrunner has two versions, here we choose "Text Interface" version. For each test that runs, junit outputs through a good user interface, so you can visually see the progress of the test. It will tell you how long the whole test took. If all the tests are not faulted, it says OK and tells you how many tests were run.
public static void Main (string[] args) {Junit.textui.TestRunner.run (Suite ());}
This code creates a testrunner and wants it to run the Filereadertester class. But when we do, we'll see:
Run the tests frequently, and take the tests into account every time you compile-at least once per day for each test. During refactoring, you can run only a handful of tests that are used primarily to examine the code that is being developed or collated today. Yes, you can run a handful of tests, which is sure to be faster, or the whole test will slow down your development and make you hesitate to do so. What happens if the test goes wrong? In order to show that I intentionally introduced the bug, so that the test file could not be found. will see:
This allows us to quickly find out where the error is and modify the code. When writing test code, we should let it fail first. The reason for doing this is to prove that the test mechanism can actually run and that the test did test what it was testing.
The detailed code that runs above gives you a better understanding of the test code:
Import Java.io.filereader;import java.io.ioexception;import junit.framework.test;import junit.framework.TestCase; Import Junit.framework.testsuite;public class Filereadertester extends TestCase {private FileReader _input = Null;public Filereadertester (String method) {super (method);} public static void Main (string[] args) {Junit.textui.TestRunner.run (Suite ());} @Overrideprotected void SetUp () throws Exception {try {_input = new FileReader ("Data.txt");} catch (Exception e) {throw NE W runtimeexception ("Cannot open test file");}} @Overrideprotected void TearDown () throws Exception {try {_input.close ();} catch (Exception e) {throw new runtimeexception ("Close test file Error");}} public void Testread () throws IOException {char ch = ' & ', for (int i = 0; i < 4; i++) ch = (char) _input.read (); Asser Tequals (' d ', ch);} public static Test Suite () {TestSuite suite = new TestSuite (); Suite.addtest (New Filereadertester ("Testread"); return Suite;} public void Testreadandend () throws ioexception {int ch = 1234;for (int i = 0; I < 141; i++) ch = _input.read (); Assertequals ( -1, ch); Comparison is equal}}
(Ⅲ) unit testing and functional testing
The purpose of the JUNIT framework is unit testing. The purpose of unit testing is to increase programmer productivity. Unit testing is a highly localized thing, and each test class belongs to a single package. It is capable of testing the interfaces of other packages, except that it assumes that all other packages are normal.
Functional testing is completely different. They are used to ensure that the software works properly. They protect quality from the customer's point of view and do not care about the programmer's productivity. They should be developed by an independent team that likes to look for bugs. The team should use heavyweight tools and techniques to help develop good functional tests.
in general, functional testing treats the entire system as if possible as a black box. In the face of a system that has a GUI to test, they operate the system through the GUI. In the face of file updates or database updates, functional testing only observes data changes caused by specific inputs.
Once a functional tester or end user finds a bug in the software, there are at least two things to do to get rid of it. Of course you have to modify the code to get rid of the error, but you should also add a unit test to expose the bug. In fact, when we receive a bug report, we should first write a unit test that causes the bug to emerge.
(Ⅳ) add more tests
Now we should add more tests. The principle we follow is to observe all the things that the class does, and then test for any one of the possible failure conditions of a function. Remember, testing should be a risk-driven behavior, and the goal of testing is to identify possible errors now or in the future. So I'm not going to test the access functions that just read or write a field, because they're too simple to be wrong. It is also important to write too many tests that often do not have enough test volume. Test where you are most worried about mistakes, so you can get the most out of your testing efforts.
(1) Consider the possible error boundary and concentrate the test firepower there.
An important technique for testing is to "look for boundary conditions". For read (), the boundary condition should be the first character, the last character, and the second penultimate character:
public void Testreadboundaries () throws IOException {assertequals ("read First char", ' B ', _input.read ()); int ch;for (int i = 0; I < 222; i++) ch = _input.read (); Assertequals ("Read Last char", 8, _input.read ()); Assertequals ("Read First char",-1, _ Input.read ());}
You can add a message to the assertion. If the test fails, the message will be displayed.
"Look for boundary conditions" also includes looking for special situations that could lead to a test failure. For file-related tests, an empty file is a good boundary condition:
public void Testemptyread () throws IOException {file empty = new file ("Empty.txt"); FileOutputStream out = new FileOutputStream (empty); Out.close (); FileReader in = new FileReader (empty); Assertequals ( -1,in.read ());}
(2) When things are supposed to go wrong, don't forget to check if the expected exception was thrown.
When testing, do not forget to check the expected error when it appears as scheduled. If you try to close the stream and then read it, you should get a ioexception, which should also be tested:
public void Testreadafterclose () throws IOException {_input.close (), try {_input.read (); Fail ("No exception for read past E nd ");} catch (IOException e) {}}
follow these rules and enrich your tests. For some of the more complex classes, it may take some time to explore their interfaces, and in this process you can really understand the interface. As the test class grows more and more, you can generate another class that is designed to contain test suites made up of other test classes.
Class Mastertester extends Testcase{public static void main (string[] args) {Junit.textui.TestRunner.run (Suite ());} public static Test Suite () {TestSuite suite = new TestSuite (); Suite.addtest (new TestSuite (Filereadertester.class)); Suite.addtest (New TestSuite (Filewritertester.class));//......return Suite;}}
(3) Do not write tests because the test cannot capture all bugs, because the tests do capture most bugs.
Object technology has a "subtle" point: Inheritance and polymorphism make testing difficult because there are many combinations that need to be tested. If you have 3 abstract classes that work with each other, and each abstract class has 3 subclasses, you have a total of 9 classes and 27 combinations to choose from. It's not about trying to test all the possible combinations, we don't have that much time, but we should try to test as many classes as possible, which can greatly reduce the risk associated with various combinations. We may have missed something, but I think "it's better to spend some reasonable time catching most bugs" than "trying to catch all the bugs in a lifetime".
This article mainly introduces the value of self-test code and uses the JUnit test framework for simple testing, in order to demonstrate that building automated testing is very important for developers. In fact, what we lack is not the ability to solve the problem, but the lack of it is difficult to find out where the problem is. Rather than spend so much time looking for a bug, it's better to kill it in the process of development. The content of this article is also biased in theory, but it has to be introduced because refactoring requires a reliable test environment, even if it is not refactoring to write test programs is also important.
PS: The next article will formally open the door to refactoring: refactoring notes-refining functions. I hope this article will be of some help to you.
Reconstructing notes--building test system