Test-driven Javascript development-4. Test-driven development process (II)

Source: Internet
Author: User
Document directory
  • Step 1: Write a test
  • Step 2: Observe the failed tests
  • Step 3: Make sure the test passes
  • Step 4: Remove duplicate refactoring
  • Step 5: repeat work

TDD is an iterative development process. It includes the following steps: 1. Write the test; 2. Run the test and observe the failure; 3. Ensure that the test passes; 4. refactor to reduce duplication.

In each iteration, testing is a specification. After the development is completed, the test will pass. Then we need to reduce repeated code and improve the refactoring of the design, then run the test again, and ensure that it passes.

Although TDD does not advocate the expected big design, we still need to make a simple design before TDD starts. How do we write our first design? When we get enough information to develop a test, writing the test code is a design. We specify the behavior of specific code under specific circumstances, how the components between systems respond, and how they are combined. The following is an example for better learning.

The iteration time In TDD is very short, and we need to be very clear about our stage. Whenever we modify the code or remove some features, we need to record them in the todo list for observation. This list can be a piece of paper, notepad, and so on, as long as you can quickly find and record. Before processing these new changes, we need to end this iteration first. After this iteration, We will extract a task from the todo list to start the next iteration.

 

Step 1: Write a test

In each TDD iteration, the first thing to do is to select a function you want to do and write unit tests for it. The unit test should be short. The test focuses on a function point on the function. It is better to write the test rule that writing as few test code as possible can make the test fail. Of course, test assertions cannot be repeated with previous tests. If a test involves two or more aspects of the system, it means that either the test is too large or contains repeated test points.

The test must be able to describe the features we have implemented. without modifying the functional code, we do not need to modify the test code.

Suppose we want to complete the development of a string. Prototype. Trim function to remove spaces before and after the string. For a good test of this method, the first step should be to test whether the space before the test is deleted.

testCase("String trim test", {  "test trim should remove leading white-space":  function () {    assert("should remove leading white-space", "a string" === " a string".trim());  }});

For the sake of rigor, we need to first judge that the string contains the trim method. This is because the global functions we add may conflict with third-party code. Adding typeof "". Trim = "function" before the code can help us find problems before running the test.

Provides input conditions for unit tests. After running, the system determines whether the output conditions are consistent with expectations. The input condition is not only a function parameter, but also any function dependency, such as global scope and special States of some objects. Similarly, output results include return values, global scopes, or related objects. We usually divide input and output items into two types: direct and indirect. Direct items: function parameters and return values; indirect items: parameters and modified external objects that are not passed in as parameters.

 

Step 2: Observe the failed tests

You can run the test after the test is ready. There are many reasons for us to run the test before writing the implementation code. The most important thing is that we can use it to determine the code status. When writing a test, we have a clear expectation about how the test will fail. Although the unit test does not have a logical branch, the code is relatively simple, but it also has bugs like other codes. But run it and compare the expected results. We will soon find these bugs.

Ideally, when we add a new test, we should be able to run all the test cases. In this way, we can easily capture Interference tests. For example, one test depends on another test.

Before writing the implementation code, run the test to tell us something new. Sometimes there is such an experience that the test can pass normally before we write any implementation code. In general, this should not happen. TDD teaches us to write tests that cannot pass. Because we first write the test code, and the function code has not yet been developed, the test code can run normally, which indicates that there is a problem. We need to determine the source of the problem, whether the running environment has provided the implementation of this method, or whether we need to keep this test case.

 

Step 3: Make sure the test passes

After preparing the failed test code, we need to write the implementation code and ensure that the test code can pass. Sometimes we even need hard coding, so we don't have to worry about how bad our code is in this step. We can optimize it later in the refactoring process. When writing the implementation code, we need to find the most obvious concise implementation method. If we don't have it, we can forge it and delay the specific implementation.

1. You don't need him

In extreme programming, the essence of TDD is "You don't need it", meaning that it is not necessary to add related functions until necessary. Adding some code that may be used in the future based on the assumption will greatly increase our code. For dynamic languages, especially JavaScript, violation of this principle is sometimes tempting to us. It can increase code flexibility. In one example, too many parameters are added to the function. Do not.

2. Test with string. Prototype. Trim

The following code is developed to meet the requirements of previous tests.

String.prototype.trim = function () {  return this.replace(/^\s+/, "");};

As you can see, this implementation is not complete, except the left space. But TDD is like this, and each step is very small, as long as it can pass the test. After discovering new requirements, write the test code, complete the implementation code, and pass the test.

3. The simplest solution to work

The simplest solution sometimes means that you may need to add hardcoded code to the product. Because sometimes the general solution may not be so obvious, we can use hard coding to promote our project and wait until there is a solution. Although hard coding can promote our project, code quality is our final goal.

Step 4: Remove duplicate refactoring

Finally, the most important and interesting job is to make the code clean. After code development is complete and the test runs smoothly, we can consider refactoring and remove some repeated code. There is only one criterion during this period: the test must be able to run. We recommend that you modify only one operation at a time and ensure that the test passes. Refactoring is to maintain and modify existing code, so the test cannot fail.

Duplicate code may appear in different locations. Sometimes it is used to solve the hard-coding solution. If we have a hard-coded counterfeit response, we need to add another test for it so that it fails under different input conditions. Maybe we can't think of a hard-coding solution at the moment, but at least we know the problem. He provides us with enough information to help us find the final solution.

Duplicate Code may also exist in the test code, such as the request object in setup and counterfeit dependencies. The test code is also code, and maintenance is also required to remove duplicate content. If the test code is too coupled with the system, we need to extract help methods and refactor the structure. Setup and teardown can be used to set the creation and destruction of objects in a centralized manner.

We cannot make the test fail during the refactoring process. If we do not use less code to complete the job during the refactoring process, we need to consider putting the work to the future.

Step 5: repeat work

Once all the work is completed, there is no repeated code, and no reconstruction work is required. In this case, find a new task from the todo list and repeat the above steps. Repeat the job as needed. After you are familiar with this process, you can zoom in, but make sure that your cycle is short, so that you can get timely feedback.

After the function meets the requirements, we can consider improving the test coverage rate, you can add a test on the boundary value, a test on the dependencies, a test on different input types, and an integrated test between different components. The following is the second test we added for string. Prototype. Trim:

"test trim should remove trailing white-space":function () {  assert("should remove trailing white-space", "a string" === "a string ".trim());}
Related Article

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.