DEV Community

Sathish for Skcript

Posted on • Edited on • Originally published at skcript.com

3

Red, Green, Refactor.

Red, Green, Refactor is a TDD approach or framework that the developers use to build the test suite first, write the implementation code, and refactor the code once the test suite passes.

According to Computer Scientist Robert C Martin who advocates TDD, we can write effective code only when there is a test case to pass and the only time we have the time to write tests is before writing the implementation code. The feasible way to do so is to use the Red, Green, Refactor approach.

What are they?

We can compartmentalize writing code into 3 segments as the title suggests.

Red - Write a test suite without the implementation code, making it fail.

Green - Now, we can write the implementation code, just so the test suite passes. Nothing more, nothing less.

Refactor - After the test suite passes, we can look for ways to optimize.

...rinse & repeat. This happens until we have a fully functional implementation code.

Robert C. Martin (“Uncle Bob”) provides a concise set of rules for practicing TDD.

  1. Write production code only to pass a failing unit test.
  2. Write no more of a unit test than sufficient to fail (compilation failures are failures).
  3. Write no more production code than necessary to pass the one failing unit test.

How is it done?

We can see how it’s done by looking at an implementation of the Fibonacci problem with recursion. We’ll use the Jest playground.

Iteration 1

We have two JavaScript files, one is fib.js to contain the implementation code, fib.test.js to contain the test suites. As an overkill, you can start without the function defined in fib.js. Let me keep the post as short as possible.

Red

Since we are solving the problem with recursion, we need to first define our base case first. Which is, if n is lesser than 2, we need to return n.

Let’s first write the test suite for the base case, which will be the following,

const fib = require("./fib");

describe("base case , n < 2", () => {
  test("n = 1 will return 1", () => {
    expect(fib(1)).toBe(1);
  });
});
Enter fullscreen mode Exit fullscreen mode

We expect this to fail since we don’t have any implementation code.

Green

We now need to write the implementation code for the base case. Remember, only the code that’s needed to make our test suite pass.

function fib(n) {
  if (n < 2) {
    return n;
  }
}
module.exports = fib;
Enter fullscreen mode Exit fullscreen mode

Now, this code satisfies our test suite. What’s next? Let’s look at refactoring the above code.

Refactor

From the above implementation code, we don’t have anything much to refactor so let’s move to Iteration 2.

Iteration 2

Red

Now we have our base case, let’s look at the next step which is to write the recursive code. Let’s extend our test suite to test the recursive case.

describe("recursive case , n >= 2", () => {
  test("n = 8 will return 21", () => {
    expect(fib(8)).toBe(21);
  });
  test("n = 15 will return 610", () => {
    expect(fib(15)).toBe(610);
  });
});
Enter fullscreen mode Exit fullscreen mode

Now, that we extended our test suite, let’s see the failing test case result which is as follows,

As you have guessed, this is because, in our implementation code, we check if n is lesser than 2 and return n. It doesn’t currently handle the case where n is greater than or equal to 2.

Green

We’ll now write the implementation code so that our test suite passes.

function fib(n) {
  if (n < 2) {
    return n;
  } else {
    return fib(n - 1) + fib(n - 2);
  }
}
module.exports = fib;
Enter fullscreen mode Exit fullscreen mode

With recursion, we have written the implementation code to handle the case where n >= 2. We’ll now have Green, where our test suite passes.

Refactor

What could we possibly do here to refactor the above code? Not much, but as a cosmetic update, we can remove the unwanted braces and the else part since we are returning in the if part. After refactoring, our code will look like the following,

function fib(n) {
  if (n < 2) return n;

  return fib(n - 1) + fib(n - 2);
}
module.exports = fib;
Enter fullscreen mode Exit fullscreen mode

Iteration 3

Well, there’s no Iteration 3 since we have a fully functional module. So, it ends here.

Conclusion

This approach might look time-consuming at first, but once we get a hold of it, it can be employed to write effective testable code that will make everyone’s life easier, while building a more robust solution than you otherwise would have.

Ok, bye!

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

SurveyJS custom survey software

Build Your Own Forms without Manual Coding

SurveyJS UI libraries let you build a JSON-based form management system that integrates with any backend, giving you full control over your data with no user limits. Includes support for custom question types, skip logic, an integrated CSS editor, PDF export, real-time analytics, and more.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay