What is test-driven development
Test-driven development refers to the development of test code before writing implementation code. JUnit's author Kent Beck said: "The key reason for writing test-driven code is to eliminate fear and uncertainty in development, because the fear of writing code allows you to be careful with your temptation to avoid communication, make you shy of getting feedback, make you restless, and TDD is the elimination of fear, An important means of making Java developers more confident and more receptive to communication. The benefits of TDD may not be immediately present, but you will find at some point that these benefits include:
- Clearer code-write only the code you need
- A better design
- Greater flexibility-encourages programmers to interface-oriented programming
- Faster feedback-don't know if the bug exists until the system is online
TDD can be used at multiple test levels, as shown in the following table:
Test level |
Description |
Unit Test |
Code in the test class |
Integration Testing |
Test the interaction between classes |
System Testing |
Testing the system in operation |
System Integration Testing |
Systems in a test run include third-party components |
Example of test-driven development
Now we need a piece of code to calculate the ticket revenue for a movie theater, the current business rules are very simple, including:
- Price per ticket (unit price) ¥30
- Revenue = Number of tickets SOLD * unit price
- Maximum occupancy of 100 people in the screening Hall
There is also a hypothesis: at present, because there is no professional equipment or system to count the number of tickets sold, in the calculation of ticket revenue, ticket sales amount is manually entered by the user.
The basic steps of TDD are: red-green-refactoring.
- Red-write a test that can't be passed
- Green-Write the implementation code and let the test pass as soon as possible
- Refactoring-Refactor the code and let the test pass again
Next we follow the above steps to complete the function of ticket revenue calculation.
PackageCom.lovo;ImportJava.math.BigDecimal;ImportOrg.junit.Assert;ImportOrg.junit.Before;ImportOrg.junit.Test; Public class ticketrevenuetest { PrivateTicketrevenue ticketrevenue;PrivateBigDecimal expectedrevenue;@Before Public void setUp() {ticketrevenue=NewTicketrevenue (); }@Test Public void oneticketsoldisthirtyinrevenue() {expectedrevenue =NewBigDecimal ("a"); Assert.assertequals (Expectedrevenue, Ticketrevenue.estimatetotalrevenue (1)); }}
The above test code not only cannot pass the test, even compiles cannot pass, because the Ticketrevenue class does not exist yet. Next we can take advantage of the IDE's Code repair functionality (both Eclipse and IntelliJ) to create the Ticketrevenue class and the Estimatetotalrevenue method for calculating ticket revenue in that class.
package com.lovo;import java.math.BigDecimal;publicclass TicketRevenue { publicestimateTotalRevenue(int i) { return BigDecimal.ZERO; }}
The
can now run your unit test cases, but because we haven't implemented the real business logic, this test is not possible, as shown in.
However, so far we have completed the "red" step. Next we modify the Estimatetotalrevenue method of the Ticketrevenue class to let the test pass.
package com.lovo;import java.math.BigDecimal;publicclass TicketRevenue { publicestimateTotalRevenue(int numberOfTicketsSold) { BigDecimal totalRevenue = BigDecimal.ZERO; if1) { new BigDecimal(30); } return totalRevenue; }}
Run the unit test again, as shown in the results.
Here, the second step of "green" is done.
Next we start refactoring the code for the Ticketrevenue class.
package Com.lovo; import java.math.BigDecimal; public class ticketrevenue { private final static int Ticket_price = 30 ; public BigDecimal estimatetotalrevenue (int Numberofticketssold) {BigDecimal totalrevenue = null ; Totalrevenue = new BigDecimal (Ticket_price * numberofticketssold); return totalrevenue; }}
The reconstructed code can calculate the corresponding revenue based on the number of tickets sold, and it has taken a big step forward compared to the previous hard code, but it is clear that it does not take into account the case where the input is less than 0 or greater than 100. So we need more test examples to simulate the possible inputs in the actual working environment, and we have made the following improvements to the test code that we just tested.
PackageCom.lovo;ImportJava.math.BigDecimal;ImportOrg.junit.Assert;ImportOrg.junit.Before;ImportOrg.junit.Test; Public class ticketrevenuetest { PrivateTicketrevenue ticketrevenue;PrivateBigDecimal expectedrevenue;@Before Public void setUp() {ticketrevenue =NewTicketrevenue (); }@Test(expected = Illegalargumentexception.class) Public void Failiflessthanzeroticketsaresold() {Ticketrevenue.estimatetotalrevenue (-1); }@Test Public void zerosalesequalszerorevenue() {assert.assertequals (Bigdecimal.zero, Ticketrevenue.estimatetotalrevenue (0)); }@Test Public void oneticketsoldisthirtyinrevenue() {expectedrevenue =NewBigDecimal ("a"); Assert.assertequals (Expectedrevenue, Ticketrevenue.estimatetotalrevenue (1)); }@Test Public void tenticketssoldisthreehundredinrevenue() {expectedrevenue =NewBigDecimal ("+"); Assert.assertequals (Expectedrevenue, Ticketrevenue.estimatetotalrevenue (Ten)); }@Test(expected = Illegalargumentexception.class) Public void Failifmorethanonehundredticketsaresold() {Ticketrevenue.estimatetotalrevenue (101); }}
Running the test again will result in a test that has two invalid inputs in 5 tests (a test with a sales quantity of-1 and 101), for the simple reason that no invalid input code has been processed in our code. Next, we'll continue to refactor our code.
PackageCom.lovo;ImportJava.math.BigDecimal; Public class ticketrevenue { Private Final Static intTicket_price = -; PublicBigDecimalestimatetotalrevenue(intNumberofticketssold)throwsillegalargumentexception {BigDecimal totalrevenue =NULL;if(Numberofticketssold <0) {Throw NewIllegalArgumentException ("Ticket sales quantity must be greater than or equal to 0"); }Else if(Numberofticketssold > -) {Throw NewIllegalArgumentException ("The number of tickets sold must be less than or equal to); }Else{totalrevenue =NewBigDecimal (Ticket_price * numberofticketssold); }returnTotalrevenue; }}
Run the test code again and check to see if your bar is green (JUnit's famous quote is "Keep your Bar Green"). Of course, for people with code cleanliness, the above code is still slightly bloated, it's OK, another refactoring it.
PackageCom.lovo;ImportJava.math.BigDecimal; Public class ticketrevenue { Private Final Static intTicket_price = -; PublicBigDecimalestimatetotalrevenue(intNumberofticketssold)throwsillegalargumentexception {if(Numberofticketssold <0|| Numberofticketssold > -) {Throw NewIllegalArgumentException ("The number of tickets sold must be between 0 and 100"); }return NewBigDecimal (Ticket_price * numberofticketssold); }}
Once you've finished modifying your code, never forget to do it again, and still need to keep your bar green.
If we use the object-oriented programming paradigm, then the refactoring of the code should follow the object-oriented design principles. The great god Robert Matin these principles into solid principles.
Principles |
English |
Description |
Single duty principle (S) |
Single Responsibility Principle |
Each object only does what it's supposed to do. |
Opening and closing principle (O) |
Open-closed Principle |
Accept extensions but do not accept modifications |
Richter replacement principle (L) |
Liskov Substitution Principle |
You can replace a parent type with a subtype |
Interface isolation principle (I) |
Interface Segregation Principle |
The interface should be small and dedicated |
Dependency reversal principle (D) |
Dependency inversion Principle |
Relies on interfaces and does not rely on implementations |
Description: The above example is from the well-grounded Java Developer (Chinese name "Java Programmer's Way of practicing"), which covers a lot of practical techniques in Java development and the new language features of Java. Interested can read this book, I believe you will get a lot of harvest.
The first glimpse of test-driven development