Unveil the secrets of extreme programming: Test-driven programming

Source: Internet
Author: User
By writingCodeWrite tests to make everything simple

For the past 50 years, testing has always been considered a task at the end of the project. Of course, the test can be carried out in conjunction with the project.AllThe Code starts after it is completed, but is generally tested later. However, XP advocates suggest completely reversing this model. AsProgramStaff, you should write the codeBeforeCompile the test, and then write the code that is sufficient to pass the test. This will help make your system as simple as possible.

Write test first
XP involves two tests: Programmer testing AndCustomer Test . Test-driven programming (also known Test first Programming) most often refers to the first test, at least when I use this term. Test-driven programming is Programmer testing (Unit Test-again, just use another term) determines the code you write. This means that you must test the code before writing it. Test indicates that you Yes Write the code Decision The code you want to write. You only need to write enough code to pass the test-no more or less. XP rules are simple: if you do not perform a programmer test, you do not know what code to write, so you do not write any code.

How to Write a test first
The entire theory is great, but howFirstWhat about writing a test? First, I recommend that you readTest-driven development: by example(See references) This book provides a detailed example throughout the entire book. This book not only describes how to write tests and how these tests drive your code, but also explains why test-driven programming is a good programming method. Here is a simple example to show you what I'm talking about.

Test driver vs. Test first
I like to use the term "test-driven", rather than the term "test first", because the first test emphasizes the principle of writing a programmer to test the code. These principles are important, but the real strength lies in the changes in ideas and programming habits implied by the test drive. The more appropriate term "test-driven programming" includes two tests, which point to the XP team's emphasis on making the test drive everything they have to do.

Assume that I am writing the includePersonObject System. I wantPersonHe/she can tell me his/her age (as an integer ). Even if I haven't written a little bit of code, I should write the test. "What ?", You may say, "I don't even know what to test. How to Write a test ?" The answer is simple.IndeedKnow what you are testing, just don'tYesWhat you know, because you are not used to thinking in this way. This is what I mean.

You do not have any code yet, but you shouldPersonThe prototype of the object.PersonThere should be a method on the object, which can return age in integer form. Because I use the Java language most often, I use JUnit to write a programmer test. Listing 1 showsPersonObject-written JUnit test:

Listing 1. JUnit test for the person object

Package COM. roywmiller. testexample; import JUnit. framework. testcase; public class tc_person extends testcase {protected person; Public tc_person (string name) {super (name) ;} protected void setup () throws exception {person = new person ();} public void testgetage () {int actual = person. getage (); assertequals (0, actual);} protected void teardown () throws exception {}}

First, let me introduce some simple principles to those who are not familiar with JUnit.TestcaseClass is the most frequently used class. You just wrote a test class (in this exampleTc_person), It isTestcase. (Note: either or not acceptable in JUnit 3.8.1StringBut since almost all of my Java Development is completed in Eclipse IDE (see references), Eclipse IDE provides me with this constructor for free, so I keep it here .) After a test class is created, the test method must have actual actions. These methods use the prefix appropriately.Test(They must bePublicAnd returnsVoid). When running the test, JUnit:

    • Saves test classes and executes each method starting with "test"
    • Execute before executing each test methodSetup ()Method
    • After each test method is executedTeardown ()Method

In this example,Setup ()Method. It is just instantiatedPerson(I am using this method to make you think this test case looks complete "). This means that if there are 20 test methods, each test method uses a newPersonThe instance starts.Teardown ()So it is empty now. You do not needSetup ()OrTeardown (); I usually do not create them until I write the second or third test method and confirm that these methods share certain public settings or destruction activities.

With these principles, I have made some design decisions in the testing method. I assume that a person can be constructed and "default"PersonThe returned value is age 0. It is also assumed thatPersonObject hasGetage ()Method. Even if those assumptions are not always true, they still apply. It can be said that this is a simple test. Let me explain the test-driven programming. With these assumptions, instantiatePerson(InSetup ()InstantiatePersonTo demonstrate how to use the setup () method, call the method being tested in the test method, and then call one of the "assert" methods. Whether the assertion method test is true. In other words, these methods make an asserted against something that tells JUnit to verify whether it is true. Table 1 lists the types of assertions:

Table 1. asserted category

asserted method description
assertequals compare whether two things are equal (basic type or object)
asserttrue evaluate the Boolean value to see if it is true
assertfalse evaluate the Boolean value to see if it is false
assertnull check whether the object is null
assertnotnull check whether the object is not null
assertsame check whether two objects are in the same instance
assertnotsame check whether two objects are not in the same instance

Here, I checkPersonWhether the instance age is 0, newPersonThe default value of the object is 0.

Of course, this test cannot even be compiled.

Apparently, I have notPersonClass, so running this test will cause a problem-JUnit provides a red bar. If you can run and pass the test, a green bar is displayed. Your goal is always to get a green bar. Don't forget that JUnit's "getting green bars and making code clean" (sometimes complaints are inevitable ).

No problem. I will createPersonClass, as shown in Listing 2:

Listing 2. Person class

Package com. roywmiller. testexample; public class person {public int getage () {return 0 ;}}

Now, when you run this test, you can see a green bar when the test passes. I mustGetage ()Return Value, otherwise it will not be compiled. It happens that 0 is the most convenient, and 0 is considered a new one.PersonThe default value of the instance. Again, I wrote only the Code required to pass the test.

EnablePersonIt is good to have the default age value, but it will not be very helpful to my system.PersonIt needs to be smarter than that. What I really need is,PersonHas his birthday and can answer his current age. This meansPersonThe age of the object increases with the passage of time. Before encodingTestgetageRenameTestgetdefaage age(Clearly, I'm testing the default age) and write another test method for this test case, as shown in listing 3:

Listing 3. New Test Method

Public void testgetage () {gregoriancalendar calendar = new gregoriancalendar (1971, 3, 23); person. setbirthdate (calendar. gettime (); int actual = person. getage (); assertequals (31, actual );}

This test cannot be compiled yet (Do you notice the mode in it ?), BecausePersonNoSetbirthdate ()Method. After this method is created,PersonIt will be similar to listing 4:

Listing 4. Updated person class

Package COM. roywmiller. testexample; import Java. util. date; public class person {protected date birthdate; Public int getage () {return 0;} public void setbirthdate (date abirthdate) {This. birthdate = abirthdate ;}}

PersonInGetage ()The test fails because there are still no changes.
 

GeneratedAssertionfailederrorThe result is 0 instead of 31. This failure was expected because I did not change the getage () method to do something different. Now, you only need to write enough code to pass the test (two tests are available here ). I must allow the default value of age to 0, but I must calculate the age of the person born on January 1, March 23, 1971. Some programmers (including Kent Beck) Suggest to make it as simple as possible, such as checkingBirthdateTo check whether it is null. If it is null, 0 is returned; otherwise, 31 is returned. Then, write another test to make the computation smarter. It is a good technique to think about the problem in a small step. We need to adopt this technology. It would be better if you want to go back to the basic procedures mentioned above to get yourself out of debugging practices. But here I want to make this example a little simpler, so I just try to useCalendarCalculate the age so that the test passes. Listing 5 showsPersonThe code I wrote in:

Listing 5. getage () Implementation

Package COM. roywmiller. testexample; import Java. util. calendar; import Java. util. date; import Java. util. gregoriancalendar; public class person {protected date birthdate; Public int getage () {If (birthdate = NULL) return 0; else {int yeartoday = calendar. getinstance (). get (calendar. year); Calendar calendar = new gregoriancalendar (); Calendar. settime (birthdate); int birthyear = calendar. get (calendar. year); Return yeartoday-birthyear;} public void setbirthdate (date abirthdate) {This. birthdate = abirthdate ;}}

When I run the test, I fail. The expected result is 31, but the actual result is 32. What's wrong? Well, I know that the problem must be found in the code I just wrote and I didn't take it further into consideration. After the checkElseAfter the clause, I understand that I only calculate the age based on years. This is not correct. I am 31 years old, but I will be 32 years old in a few days this month (but I wrote this code in February), myAlgorithmResult of the error. So you need to reconsiderGetage (). I corrected this error using the code segment in Listing 6:

Listing 6. Corrected getage ()

Else {int yeartoday = calendar. getinstance (). get (calendar. year); Calendar calendar = new gregoriancalendar (); Calendar. settime (birthdate); int birthyear = calendar. get (calendar. year); If (yeartoday = birthyear) return yeartoday-birthyear; elsereturn yeartoday-birthyear-1 ;}

Green bars! InPersonThe class contains some repeated code, but I leave it for later refactoring exercises. You are welcome to clean up the code for me. You can do this with confidence because you can run a test to prove that you have not broken anything.

This example shows what test-driven programming is like. I write enough code in each step to pass the test. As a theory, this is a challenge in ideological tendencies. You must get used to this idea and write codeBefore, And you can write a test. After all tests are passed, the work is completed.

When writing a test, you must be used to deliberately looking at it. The example in Listing 6 is quite simple. Even the simplest programming problems are usually more complex in practice. This method helps to break down the problem into more manageable parts, but you may still encounter some complicated headaches in the end. In those cases, you must avoid thinking too far. Do not assume that it is "universal" or that this method can handle some situations that have not yet been encountered. Write a test to make it pass. You need to take a few smaller steps and then write a test that forces you to take more steps. Remember that you are testing the code existence. If you write the code in a small step, you are doing the right thing.

Why should I write a test ......
Maybe it is a good idea to write a test first. It seems strange, or it may seem unnecessary. I often hear the second reason from experienced programmers. These programmers are very smart and have a lot of experience. They say they don't need to write tests first because they know what they are doing. I can understand, but I suspect they have an implicit assumption: They don't understand writing tests first. I cannot agree. In fact, I think there are three reasons for writing the test method first:

    • Learning
    • New Problem
    • Confidence

Writing tests first-then executing these tests later-is a better way to learn. It enables you to focus on the Interface part of the code you write. When writing a test, assume that the class you are using already exists, and then use the class in the way you want to use it in the rest of the system. Later, when you forget how to use the class, you can view the test and take a very specific example. This is a good way to learn.

One of the most interesting things about writing a test first is that it helps identify new problems. You are "Growing" the system you are creating. If you are using XP, You do not design the whole thing in advance-but design it while developing. When you write a test and pass the test, you are asking the code to tell you what it wants to do and what it will become. If you just start coding, you will do exactly what you want. The later you make a decision, the more likely you will find new problems and new trends, which can improve your system.

However, the benefit of writing tests first is that these tests will be executed later. When I write a test first, I have many strange logics. I may not cover all aspects of the code, but may include many of them. In any case, I will have a set of tests, which is better than most projects I have participated in that do not use XP. I can press a button to run these tests. A few seconds later, I will know whether the code runs in the way I tell it. This regression tool is very valuable. Anyone in my team (or anyone anywhere) can change the code at any time, even on the day before the code is released, because the test will immediately tell them if there are any problems. As a programmer, this gives me more confidence than most programmers.

Helping shape the future of "unlocking the mysteries of extreme programming"
As always, I would like to invite you to the next columnArticleProvide your feedback to facilitate this column. What are your biggest problems with XP? Do you think it is completely stupid, unwise, non-professional, or impossible? What is the most confusing practice? Please make your suggestion in the forum of this article, or send me an email directly.

...... Why are people not using
Many programmers who do not write tests first do not even know that this method can be used. If they know, they may be confused about how to use it, or they may want to know why. Even if they know how to do it and think it is a good idea, many people still do not write tests first.

First, you must follow certain procedures for writing and testing. As a programmer, I think it is easier for me not to write tests for my work being developed. This is true sometimes, but usually only in the short term. If I do not write tests frequently, a pile of code will not be tested soon. When writing the next system feature, it may be abnormal. What is the problem? I cannot answer this question without testing. Even if everything seems to work well, I cannot ensure that problems that have not occurred in the system in the past will not appear in the future. This vicious circle is why most programmers hate testers telling them why the code is faulty. In the absence of tests, tracking errors cause overtime and dissatisfaction with the work.

When I explained this to most programmers in that way, they thought test-driven programming was a good idea-and they still didn't use this method. Writing a test before writing the code means that there is no really interesting part of the work before the test fails. Do not fall into this trap, otherwise you will pay a lot in the future.

Difficult to handle
When people start to write tests, they always encounter such a situation: they say, "but there is no way to test ". XPCommunitySome may say, without testing, never write code. You should try to do this, but in my personal experience, sometimes I find that I cannot do this in some places. Should you give up if you find yourself in this situation? To a certain extent. I think you can do two things:

    • Compile the code before writing the test.
    • In rare cases, the test is not written at all and is written later.

If you find that you cannot write a test after you try to write the test, return to the test. I still want to perform the test so that I can gain confidence from the complete regression suite, but I have to write some code first and then write the test. Sometimes I write a little code and then write a little test so that the two can go hand in hand. In a few cases, I just don't know how to write a test. When this happens and my pairing partner cannot work out, I ask other people (for example, another partner) to see if they have any clever ideas. Sometimes this works. However, in some cases, the entire team is in trouble. In those cases, You must select feasibility. I may pause coding, get into trouble, or write some code without testing, and then write the test later. Maybe the first error in the Code will make the test and how to test more explicit. These are feasible rules.

test tools and technologies
in this world, almost every language has an xunit library. For the Java platform, JUnit assumes this role. I personally use Eclipse IDE (see references), which integrates JUnit very well. Eclipse is open source code and has its own test suite. You can use it. With this suitable tool, you can write a lot of good tests. But sometimes it is better to have some other help. Fortunately, some coding techniques can be used for more convenient testing, or even testing looks untestable. Some techniques available include objectmother mode, mock object and pseudo (Sham) object.

Objectmother Mode
The objectmother mode is actually implemented in the Gang of Four Abstract Factory mode (see references). It tells you to create a factory object to provide an instance for testing the object. For example, assume that you are building a system for handling customer booking lectures. You may build an objectmother objectSeminarObjects have different features that you can use to test certain situations. In Java, you may createTf_seminarObject, which has several static factory methods-perhaps calledCreatesomethingOrNewsomething. Replace "something" with some descriptions of the transaction being created, suchNewfullyloadedTo create a data member with all the data members, and the data members are filled with known data.Seminar. In this way, the test data is put in one place, which makes the code clean and easier to refactor. In the codeSeminarYou can perform the test as in listing 7:

Listing 7. objectmother example

Seminar seminar = tf_seminar.newfullyloaded; seminar. dosomething (); assertequals ("expectedvalue", seminar. getvalue ());

Imitation object
The simulated object allows you to simulate the object for testing (see references ). An interface is provided for the simulated object. You want the actual component to have this interface, and then use the simulated object until the actual component is formed. But the imitation object is not only the stub of an existing component. You can evaluate how the code interacts with a simulated object (for example, verify the number of times a method is called and check the status ). Recently, the imitation objects have been vigorously promoted, but I think they have been abused, and they are too "heavy" to be impractical.

Pseudo object
Sometimes I want a pseudo object that implements the same interface as a real object and can answer some specific questions about how I interact with it in the test. This is a pseudo object-a lightweight method used to disguise the objects in the test. A pseudo object can be any object you need. It is the most comprehensive and flexible tool, mode, and way of thinking I have ever used. I recommend that you use it. For examplePersonThe object's pseudo object looks like in listing 8:

Listing 8. pseudo object of person

Protected class shamperson extends person {protected Boolean getagewascalled; Public int getage () {getagewascalled = true; return 25 ;}}

If feasible, I always try to use a pseudo class that is testing the class (here isShamperson) As the internal class in the test. By doing so, this pseudo class is hidden from other things that do not need a pseudo class.

Once a pseudo object exists, I can directly testPersonTo test how other code worksPersonIt is used in the test of instance interaction. I can instantiateShamperson, Then interact with it, And then assertGetagewascalledTrue.

Programming revolution
Writing and testing before writing code greatly changes my life as a programmer. It can also change your life. My code is always simpler, cleaner, and more robust than the code written before the test. Remember this procedure-consider how to test the code before writing it-to make the Code better. If each software development team does not adopt other XP practices and only writes tests first, the software development world will be surprisingly better. With this practice, Any programmer can write tests first. These tools (such as JUnit and eclipse) are free of charge and are only available for you to practice. I have seen that the investment has received a timely return, and I believe you will do the same.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.