Book Image

Test-Driven JavaScript Development

By : Ravi Kumar Gupta
Book Image

Test-Driven JavaScript Development

By: Ravi Kumar Gupta

Overview of this book

Initially, all processing used to happen on the server-side and simple output was the response to web browsers. Nowadays, there are so many JavaScript frameworks and libraries created that help readers to create charts, animations, simulations, and so on. By the time a project finishes or reaches a stable state, so much JavaScript code has already been written that changing and maintaining it further is tedious. Here comes the importance of automated testing and more specifically, developing all that code in a test-driven environment. Test-driven development is a methodology that makes testing the central part of the design process – before writing code developers decide upon the conditions that code must meet to pass a test. The end goal is to help the readers understand the importance and process of using TDD as a part of development. This book starts with the details about test-driven development, its importance, need, and benefits. Later the book introduces popular tools and frameworks like YUI, Karma, QUnit, DalekJS, JsUnit and goes on to utilize Jasmine, Mocha, Karma for advanced concepts like feature detection, server-side testing, and patterns. We are going to understand, write, and run tests, and further debug our programs. The book concludes with best practices in JavaScript testing. By the end of the book, the readers will know why they should test, how to do it most efficiently, and will have a number of versatile tests (and methods for devising new tests) to get to work immediately.
Table of Contents (16 chapters)
Test-Driven JavaScript Development
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Understanding test-driven development


TDD, short for test-driven development, is a process for software development. Kent Beck, who is known for development of TDD, refers to this as "rediscovery." Someone asked a question (Why does Kent Beck refer to the "rediscovery" of test-driven development?) about this. Kent's answer to this question on Quora can be found at https://www.quora.com/Why-does-Kent-Beck-refer-to-the-rediscovery-of-test-driven-development.

"The original description of TDD was in an ancient book about programming. It said you take the input tape, manually type in the output tape you expect, then program until the actual output tape matches the expected output. After I'd written the first xUnit framework in Smalltalk I remembered reading this and tried it out. That was the origin of TDD for me. When describing TDD to older programmers, I often hear, "Of course. How else could you program?" Therefore I refer to my role as "rediscovering" TDD."

If you go and try to find references to TDD, you would even get few references from 1968. However It's not a new technique, though it did not get so much attention at that time. Recently, an interest in TDD has been growing, and as a result there are a number of tools on the Web. For example, Jasmine, Mocha, DalekJS, JsUnit, QUnit, and Karma are among these popular tools and frameworks. More specifically, test-driven JavaScript development is becoming popular these days.

Test-driven development is a software development process, which enforces a developer to write a test before production code. A developer writes a test, expects a behavior, and writes code to make the test pass. It is needless to mention that the test will always fail at the start. You will learn more about the life cycle later in this chapter.

The need for testing

To err is human. As a developer, it's not easy to find defects in our own code, and often we think that our code is perfect. But there is always a chance that a defect is present in the code. Every organization or individual wants to deliver the best software they can. This is one major reason that every software, every piece of code is well tested before its release. Testing helps to detect and correct defects.

There are a number of reasons why testing is needed. They are as follows:

  • To check if the software is functioning as per the requirements

  • There will not be just one device or one platform to run your software

  • The end users sometimes perform an action as a programmer that you never expected, and these actions might result in defects. To catch these defects, we need testing

There was a study conducted by the National Institute of Standards and Technology (NIST) in 2002, which reported that software bugs cost the U.S. economy around $60 billion annually. With better testing, more than one-third of the cost could be avoided. The earlier the defect is found, the cheaper it is to fix it. A defect found post release would cost 10-100 times more to fix than if it had already been detected and fixed pre-release.

The report on the study performed by NIST can be found at http://www.nist.gov/director/planning/upload/report02-3.pdf.

If we draw a curve for the cost, it comes as an exponential. The following figure clearly shows that the cost increases as the project matures with time. Sometimes, it's not possible to fix a defect without making changes in the architecture. In those cases, the cost is sometimes so great that developing the software from scratch seems like a better option.

Types of testing

Any product, website, or utility, should be very well tested before it is launched. There are a number of testing techniques used to test software. From a block of code to system integration testing, every kind of testing is performed for a zero-defect system. Test-driven development is more about unit testing. It helps to learn about defects in a system at a very early stage. You will learn more about unit tests and testing concepts in the next chapter.

The following figure shows the cost of detecting and fixing defects in different testing types:

As we can clearly see, if unit testing is performed, the cost of correcting a defect is close to correcting defects at the time of implementation. Since TDD enforces writing unit tests, the cost of the overall project is reduced significantly by reducing the number of defects after release.

The life cycle of TDD

The life cycle of TDD can be divided into several steps. Each time a change is to be made into the system, tests cases are written first and then some code to make the tests pass, and so on. Let's get a closer look at the life cycle. See the following figure:

The different phases of the life cycle of TDD can be explained as follows:

  • Write a test: After a set of requirements is captured, one requirement is picked and tests are written for that. Implementation of every new feature or every change begins with writing tests for it. The test can also be a modified version of an existing one.

    This is a major difference between TDD versus other techniques, where unit tests are written after the code is written. This makes the developer focus on the requirements before writing the code. This also makes it a test-first development approach.

  • Run the test and see if the test fails: A new test should always require some code to be written to make it pass. This step validates that the new test should not pass without requiring new code. This also verifies whether the required feature already exists or not. This step helps the developer increase confidence that the unit test is testing the correct constraint, and passes only in the intended cases.

  • Write minimal production code that passes: In this step, the developer writes just enough code to pass the test. The code written in this stage is not supposed to be perfect. Since the code will be improved and cleaned at a later stage, the code as of now is acceptable. At this point, the only purpose of the written code is to pass the test.

  • Run all tests: Now, we run all the test cases at once. If every test passes, the programmer can be confident that the new code works as required and does not break any existing functionality or degrade the system anyhow. If any of the tests fail, the new code must be corrected to make the test pass. Usually, all our tests should be atomic and should not fail even if a new code is added, but that's not always the case. It may happen that someone else in the team added some code or some changes were made to the existing code. So, it's always a good practice to run all tests to make sure everything works fine as it should have.

  • The cleanup code: At the end of development, there would be thousands of such test cases and code base that grows and can become complex if it is not cleaned up regularly. Refactoring is much needed. Duplicate code must be removed. Variables, functions, and so on should follow standards, and names should clearly represent their purpose and use. Code comments should be added for readability and documentation purpose.

    Removal of duplicate code applies to both production and test code. It is a must to ensure that refactoring does not break any existing features.

  • Repeat: The preceding steps give a somewhat stable code in just one iteration, which works for some defined functionalities. The whole process is now repeated for another requirement. In case new tests fail, the developer can revert the changes or undo the operation performed. This gives a rapid feedback to the developer about the progress and problems in the system.

TDD microcycle

The TDD life cycle is also defined as Red-Green-Refactor, which is also called as the microcycle.

This process can be illustrated as shown in the following figure:

There are only three steps in this microcycle:

  • Write a test that fails at the start (Red): First of all, simple test cases are written, which test the code yet to be implemented. At this step, the test will always fail (In the figure, it has been colored red).

  • Write just enough code to pass the test (Green): The developer then writes the code to pass the test. The code is written in the simplest manner and is just sufficient to pass the test.

  • Refactor the code (Refactor): Before implementing a new feature, developers refactor the old code, which just passed, for clarity and simplicity. This step improves the code quality.

This microcycle provides rapid feedback to the developer. As soon as a change is made to the system, it is tested. If there is any error, it's detected as soon as possible and fixed.