Test cycle of TDD

Posted by Sam on Sun, 03 Nov 2019 06:28:31 +0100

Writing cycle of Test Driven Development

(simple overall grasp)

You should know that without testing, there is no functional code. How to start test driven development? Write a new test first

The outline is as follows:

  1. Add a test;

    /**
    Understand that the classes and methods used in this test do not exist. At this time, the test and even the compilation cannot pass!
    */
    @Test
    public void testMovieRating() {
        Movie starWars = new Movie("Star Wars");
        starWars.addRating(3);
        starWars.addRating(5);
        assertEquals("Bad average rating", 4, starWars.getAverageRating());
    }
  1. Run all tests and view the failed ones;

    /**
    By running all the tests, we will receive an error message that tells us the existing problems in the test. It can be thought as follows:
    1.There is no Movie class
    2.addRating() does not exist
    3.getAverageRating() does not exist
     How to solve it? It's very simple. Follow the prompts and use the powerful functions of the development environment (compilers such as IDea) to quickly create a Movie class and method.
    */
    public class Movie {
        public Movie(String name) {}
        public void addRating(int newRating) {}
        public int getAverageRating() { return 0;}
    }

As you can see from the above example, all classes and methods are a shell, only providing external behavior, and internal implementation is dumb (Methods with return value should initially return a simple default value!). As you can imagine, now that the test has passed the compilation, what about running it again? Failure! Tip us: what Bad average rating expects is 4, but actually 0 In order to make the test pass, we have to "do the trick"! Now that we want 4, let's do this:

public int getAverageRating() { return 4;}

Now, rerun all! Test: pass!! The next thing to do is refactor.

  1. Make small changes to the test (original words in the book - Test Driven Development Kent Beck) [why not make small changes to the function code, how to test it.. ]
  2. Remove duplicates by refactoring. (repetition refers to the repetition between the data in the test and the data in the program)

    /**
    The above function code can only complete the expected 4 test. To make programs more general, we need to generalize them.
    The goal is to enable the program to calculate and return the average movie score. So we need to think about the constant 4 in the program:
      To generalize, you cannot be a constant (see here), so replace it with a variable.
      4 Where does it come from? It can be imagined that there is a scoring method addRating() in the Movie class,
      So 4 = (3 + 5) / 2, i.e. average score = total score / scoring times. Therefore:
    */
    public class Movie {
        private int totalRating = 0;
        private int numberOfRatings = 0;
        public Movie(String name) {}
        public void addRating(int newRating) { 
            totalRating += newRating;
            numberOfRatings++; 
            /**
             From the top down, we have made a lot of changes. Remember: the changes should have been retested, but the changes from the front to here are obvious,
    It won't make mistakes, so it's not necessary to retest every new sentence of code.
    But now we have designed a lot of changes to a method. I think it's time to run the test again. I hope there won't be any problems.
            */
            //Good,,, the test works! Then continue to modify
        }
        public int getAverageRating() { 
            // return 4;
            return totalRating / numberOfRatings;
            // Just now we have replaced the constant! Does the test still work? Run,, pass!
            /**
            Now that the example is over, you can choose to add another test to verify whether the functional code is really generalized by us.
            At the same time, it can be extended from this to see whether we can consider when we return 4
            Write such a new test with a different average score (for example, the test average score is 5), and then
            What about generalizing functional code to test that it works?
            So you have a choice. It depends on whether you are willing to go when return 4 is a constant
            Write a new test with the same purpose but different expected results to force you to generalize the functional code;
            Or would you like to write a new test later to check whether it is generalization!
            */
        }
    }

Understand: the point of test driven development is not what measures have been taken, but the ability to make these small progress itself. How small is the progress? It even allows us to control every step forward in a very small range, such as just creating a class shell, method shell, field creation, such a small step forward) because if you can make a small improvement, you can certainly complete the improvement of the required norms.

From the above example, three strategies are summarized to make the indicator bar turn green rapidly:

  1. Pseudo implementation: that is, a reflection of those small progress, by creating a shell, directly returning a constant value expected by the test to make the test pass, and then gradually replacing it with a variable!
  2. Use explicit implementation: type the real code implementation directly.
  3. Triangulation: the purpose is to make us look at problems from different perspectives, so as to get results. For example, in order to write functional code to judge the equality of two objects, we can also test the inequality!

In practice, when TDD is used, pseudo implementation and explicit implementation are usually used alternately: when everything becomes smooth, use explicit implementation; once the test fails, a red indicator bar appears unexpectedly, make a backup and return to the pseudo implementation!

END

November 3, 2019, 11:26

Topics: Java shell