Test Driven Development (TDD): Výhody, principy a praktický příklad vývoje řízeného testy

Test Driven Development (TDD): Výhody, principy a praktický příklad vývoje řízeného testy

One of the newest ways to code is so called "Test driven development" or TDD for short.
What is it about? What are its benefits compared to good old development? These questions and more will be answered in this article.

TDD

The simplest way to explain would be to point its name out, it's development that's driven by tests. But what does it mean?
What is a test? A test is a piece of code, tasked with finding out whether a certain block of code works the way it's supposed to. Most often we calculate what should the result be given an input, and then just check that it indeed returns the expected value. If it doesn't/returns an unexpected one/crashes, the test fails.

What does a failing test mean?

A failing test is in essence the same thing as an error message during compilation - something is wrong. Tests often help us discover these errors in time and make fixing them easier.
But that's not the only reason for failing tests to exist. They drive our development, afterall.

What does the development look like?

In TDD, we begin by analyzing the problem, then invent a solution and finally write the code.
However, TDD's code writing is different, as it has to follow a few laws:

  • You are not allowed to write any production code unless it is to make a failing unit test pass
  • You are not allowed to write any more of a unit test than is sufficient to fail
  • You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

It might sound strange, but the first test should be written before the file we're testing is even created. Compilation errors are errors afterall, which we want in the beginning.
Once we have this failing test, we write just enough code so it passes.
Beware! It's important to write tests before code, tests written afterwards might not be complete!
So if we wanted to develop a function that added two numbers together, our process could look like this:

  1. Write a test, that ensures a function exists.
function additionTest(){
  console.assert(addNumbers(2, 3) === 5, "Test failed");
}

Method console.assert() checks, if its first argument is true, if it is not, it throws an error with the error message provided as the second argument. Here, our program would fail because addNumbers() is not defined. We want our test to pass, so we need to implement it. And remember - keep it as simple as possible.

2. We write as little code as possible for the test to pass.

function addNumbers(a: number, b: number): Number {
  return 5;
}

function additionTest(){
  console.assert(addNumbers(2, 3) === 5, "Test failed");
}

Great, all our tests are now green - passing. Which means, we've completed our task.
Not really, if you paid attention, you might've noticed that the function "addNumbers()" doesn't really do what we'd want it to do. After we write enough code for test to pass, we move on to refactoring - we clean up the code. There's nothing to clean here, so we move back to writing failing tests.

3. Make ourselves write the function correctly.

function addNumbers(a: number, b: number): Number {
  return 5;
}

function additionTest(){
  console.assert(addNumbers(2, 3) === 5, "Test failed");
  console.assert(addNumbers(3, 3) === 6, "Test failed");
}

Suddenly, a test is failing, so we need to go write code.

function addNumbers(a: number, b: number): Number {
  return a + b;
}

function additionTest(){
  console.assert(addNumbers(2, 3) === 5, "Test failed");
  console.assert(addNumbers(3, 3) === 6, "Test failed");
}

Beautiful, everything is green, no refactoring is needed, we've completed our task.

Why TDD?

Why should we use TDD if it makes us write twice as much code?
Well, in simple applications like this one, it's hard to see, but code eventually rots.
It rots, because noone dares to maintain it, as any change may break it without anyone noticing until it's too late. That's where TDD shines - after we develop a feature, we run the test, and if they pass, we ship.

Conclusion

TDD is a great tool for writing code that lasts. To inherit code without tests after a colleague who has left the company is a very frightening experience I wouldn't wish to anybody. Inheriting code with good test coverage is a whole different story. That's why the time we spend writing tests, we eventually get back tenfold in reduced debugging.
So if you haven't tried TDD yet, it's about time to give it a shot.

Leave a Reply to %s

Your email address will not be published. Required information is marked *

en_USEnglish