TDD (Test Driven Development)?

01. Preface

A long time ago, I saw the three uppercase English letters of TDD on the Internet. It is the abbreviation of the three words Test Driven Development, which means "test-driven development"-it sounds very good. idea.

The idea is mainly to ensure two things:

  • Make sure all needs are catered for .
  • In the process of continuous code addition and refactoring, all functions can be checked for correctness .

But for a long time afterwards, I never heard the news of TDD again. Some people say that TDD is dead, and the opinions given are as follows:

1) In general, developers should not write code without failing test cases - this seems reasonable, but it can lead to overtesting. For example, in order to ensure the correctness of one line of production code, you have to write 4 lines of test code, which means that once this line of production code needs to be modified, you have to modify those 4 lines of test code.

2) The code written in order to follow TDD is easy to enter a misunderstanding: the code is to meet the test, while ignoring the actual needs.

02. What exactly is TDD?

Regardless of whether TDD is dead or not, let's first review what TDD is.

The basic idea of ​​TDD is to write test code before developing functional code. That is to say, after it is clear to develop a certain function, first think about how to test this function, and complete the writing of the test code, and then write the relevant code to meet these test cases. Then add other functions in a loop until the development of all functions is completed .

The basic process of TDD can be broken down into the following six steps:

1) Analyze the requirements and split them into specific tasks.

2) Take a task from the task list and write a test case for it.

3) Since there is no actual functional code, the test code is unlikely to pass (red).

4) Write the corresponding function code and pass the test code as soon as possible (green).

5) Refactor the code and ensure that the tests pass (refactoring).

6) Repeat the above steps.

The above process can be represented by the following figure.

 

03. The practice process of TDD

Under normal circumstances, we are used to writing functional code as soon as possible after the requirement analysis is completed, and then calling and testing.

TDD is different, it assumes that we already have a "test user", who is the first user of the functional code, although the functional code is not yet perfect.

When we write test code from the perspective of "test user", what we have to consider is, how should this "test user" use the functional code? Do you call the method directly through a class (static method), or build an instance of the class to call the method (instance method)? How does this method pass parameters? How to name the method? Does the method have a return value?

After we have the test code, we start to write the functional code, and we need to change the test from "red" to "green" as quickly as possible. Maybe the functional code at this time is not elegant, but it doesn't matter .

When the test is passed, we can safely and boldly "refactor" the functional code—optimize the ugly, bloated, and performance-biased code.

Next, suppose we received a development requirement:

Wang Wang team is going to the small town of Adventure Island to perform, and the ticket is 99 yuan. Wang Er, the only programmer on Adventure Island, needs to develop a small program that can calculate ticket revenue.

According to the TDD process, Wang Er needs to use Junit to write a simple test case. The test expectation is: the income from selling a ticket is 99 yuan.

public class TicketTest {

    private Ticket ticket;

    @Before
    public void setUp() throws Exception {
        ticket = new Ticket();
    }

    @Test
    public void test() {
        BigDecimal total = new BigDecimal("99");

        assertEquals(total, ticket.sale(1));
    }

}

In order to facilitate the compilation to pass smoothly, Wang Er needs a simple Ticket class:

public class Ticket {

    public BigDecimal sale(int count) {
        return BigDecimal.ZERO;
    }

}

The result of running the test case is shown in the figure below. Red indicates that the test failed: the expected result is 99, but the actual result is 0.

Next, Wang Er needs to pass the test quickly. Ticket.sale() The result of the modified method is as follows:

public class Ticket {

    public BigDecimal sale(int count) {
        if (count == 1) {
            return new BigDecimal("99");
        }
        return BigDecimal.ZERO;
    }

}

Run the test case again, the result is shown in the figure below, green means the test passed: the expected result is 99, the actual result is 99.

Green, green, the test passed, and it's time to refactor the functional code. $99 is a magic number and should at least be declared as a constant, right?

public class Ticket {
    private final static int PRICE = 99;

    public BigDecimal sale(int count) {
        if (count == 1) {
            return new BigDecimal(PRICE);
        }
        return BigDecimal.ZERO;
    }

}

After refactoring, run the test case again to ensure that the test passes, and add a few more test cases, such as the case where the ticket sales are negative, zero or even one thousand.

public class TicketTest {

    private Ticket ticket;

    @Before
    public void setUp() throws Exception {
        ticket = new Ticket();
    }

    @Test
    public void testOne() {
        BigDecimal total = new BigDecimal("99");

        assertEquals(total, ticket.sale(1));
    }

    @Test(expected=IllegalArgumentException.class)
    public void testNegative() {
        ticket.sale(-1);
    }

    @Test
    public void testZero() {
        assertEquals(BigDecimal.ZERO, ticket.sale(0));
    }

    @Test
    public void test1000() {
        assertEquals(new BigDecimal(99000), ticket.sale(1000));
    }

}

When the sales volume is negative, Wang Er hopes that the function code can throw an exception; when the sales volume is zero, the calculation result of the function code should be zero; when the sales volume is one thousand, the calculation result should be 99000.

There are two test cases that fail, so Wang Er needs to continue to modify the function code, the adjustment is as follows:

public class Ticket {
    private final static int PRICE = 99;

    public BigDecimal sale(int count) {
        if (count < 0) {
            throw new IllegalArgumentException("销量不能为负数");
        }

        if (count == 0) {
            return BigDecimal.ZERO;
        }

        if (count == 1) {
            return new BigDecimal(PRICE);
        }

        return new BigDecimal(PRICE * count);
    }

}

Run the test cases again and find that they all pass. It’s time for refactoring again. When the sales volume is zero or greater than or equal to one, the code can be merged, so the refactoring results are as follows:

public class Ticket {
    private final static int PRICE = 99;

    public BigDecimal sale(int count) {
        if (count < 0) {
            throw new IllegalArgumentException("销量不能为负数");
        }

        return new BigDecimal(PRICE * count);
    }

}

After refactoring, run the test cases to ensure that the refactored code is still available.

04. Finally

The following conclusions can be drawn from the above practice process:

What TDD is trying to do is give us confidence in our code, because we can test it to see if it's correct.

In other words, the key part of the TDD process is how to write effective test code. Here are 4 principles for reference:

1) The testing process should simulate the process of normal use as much as possible.

2) Branch coverage should be done as much as possible.

3) The test data should try to include real data and boundary data.

4) Test statements and test data should be as simple as possible and easy to understand.

Note that these 4 principles apply not only to TDD, but also to unit testing under any process.

Finally, what I want to say is that regardless of whether TDD is dead or not, TDD is not a silver bullet and cannot be suitable for all scenarios, but this should not be a reason for us to reject it.

[The most comprehensive and easy-to-learn station B] The ten-year boss finally sorted out the test development route, Xiaobai will learn it as soon as he learns it, no thanks for taking it away, free prostitution is allowed! !

Guess you like

Origin blog.csdn.net/dad22211/article/details/132344003