My answer to those who believe that the value of TDD is exaggerated

Once I talked to a developer from a client company about software. I should understand that the conversation went somewhere wrong when the interlocutor said about how we, the software developers, were lucky: “We trick organizations into paying us for what seems to be simple work.” It doesn’t matter how much someone has advanced in writing code, but I believe that it’s not worth talking about the entire software development industry as something like a gang of scammers.

I did not focus on this, the conversation got to Agile. The client, in general, was open to the idea of ​​testing new methodologies and improving their work processes. But - only until I mentioned development through testing (TDD, Test-Driven Development). The only answer to this was the following phrase: "The TDD value is exaggerated."



Not only was it painful for me to hear it, but it made me realize that TDD is another one of those Agile methodologies that might look like something like “urban legends”. This is what made me write this material, in which I would like to appeal to those who doubt the value of TDD.

What is TDD?


Let me start by defining TDD. This is a software development strategy, during the implementation of which, when creating a program, they are completely guided by the writing of tests. In fact, this is understandable from the name of this methodology. This idea was proposed by Kent Beck in his book Development Through Testing, which was written in 2003. Using TDD involves the following three steps:

  1. Writing a test that fails for a small piece of functionality.
  2. Implementation of the functional that leads to the successful passing of the test.
  3. Refactoring old and new code in order to maintain it in a well-structured and readable state.

This process is also known as the “Red, Green, Refactoring” cycle.

Here is a simple example of using TDD. Here we want to write a method in Python that is designed to calculate a variable Cby the Pythagorean theorem.

Here is the test code (file test.py):

#!/usr/bin/env python

"""
test.py
"""

import unittest

from main import *

class TestDrivenDevelopment(unittest.TestCase):

        def test_pythagorean_theorem(self):
                a, b = 3, 4
                self.assertEqual(calculate_side_c(a, b), 5)

if __name__ == '__main__':
    unittest.main()

Here is the method code ( main.py), in which nothing happens so far:

#!/usr/bin/env python

"""
main.py
"""

def calculate_side_c(side_a, side_b):
    return True

Now try to run the test.


The test failed (the "Red" part of the TDD cycle)

Rewrite the code, bringing the contentsmain.pyto the following form:

#!/usr/bin/env python

"""
main.py
"""

def calculate_side_c(side_a, side_b):
    c_squared = (side_a * side_a) + (side_b * side_b)
    c = c_squared ** 0.5
    return c 

Run the test again.


The test went well (the “Green” part of the TDD cycle)

Now wemain.pyrefactor thecode:

#!/usr/bin/env python

"""
main.py
"""

def calculate_side_c(side_a, side_b):
    return ((side_a ** 2) + (side_b ** 2)) ** 0.5


The test was successful for the code subjected to refactoring (Part “Refactoring” of the TDD cycle)

17 years have passed since the publication of the development book through testing. But even today, in 2020, there is still no definitive answer to the question that is very important for many people about whether the benefits of TDD are worth the time and effort to implement this methodology. As a result, many developers do not even try to develop through testing.

In support of TDD, I asked developers why they did not use this methodology. Here are 4 answers that I have heard most often:

  1. We have a QA department. Writing tests is their job.
  2. Creating tests in which moki and stubs may well be needed can take a lot of time and energy.
  3. TDD has no advantages over conventional development.
  4. TDD is slow.

Let's analyze these ideas.

We have a QA department. Writing tests is their job


This answer to my question about why someone is not using TDD will always make me laugh a little. I am a big supporter of the principle “ You build it, you run it ”, when the one who wrote the code is responsible for it. Therefore, it disturbs me when I see that developers behave as if their only task is to write functional code.

I am deeply convinced that developers are responsible for ensuring that their code has the following qualities:

  • Correctness - meeting business needs.
  • Clearness.
  • Testability.
  • Extensibility.
  • Simplicity.

The transfer of the task of writing tests to the QA department, I believe, does not belong to the list of job responsibilities of the developer. The QA department specialists have more important matters. They should not spend their working time, mainly, on testing the code using the "black box" method.

Creating tests in which moki and stubs may well be needed can take a lot of time and energy


I understand the feelings of those who say so. For example, you need to implement a method that takes three different input values. Each of them is an object, and each of these objects does not have optional attributes. All this needs to be tested by checking the different options for the method. In order to do this, you need to configure stubs and mokas , as well as write a large amount of additional code. And all this is for testing just one method. I have already been in similar situations. But I, however, see all this from another perspective.

When I think about reducing the effort and time spent on writing tests, I come up with the principles of SOLID programming and the testing pyramid.

Get started with SOLID from here.. Here, I would like to dwell on the fact that in the abbreviation SOLID is represented by the letter S, that is, on the principle of the sole responsibility (The Single Responsibility Principle). This is an idea according to which methods and classes should solve only one problem. They must function so that their activities do not affect the rest of the system.

I understand that most methods in enterprise software perform tasks that are more difficult than finding the sum of several numbers passed to these methods. But one cannot deny the fact that the use of methods aimed at solving one problem in a project greatly facilitates testing.

Let's talk about the testing pyramid.


Testing pyramid (image taken from here )

The testing pyramid is often used to find the correct ratio of different types of tests in a project.

User interface tests (UI Tests) and service tests (Service Tests) require quite a lot of time for their implementation. Therefore, the bulk of the tests should be represented by unit tests (Unit Tests), since it takes milliseconds to execute even large numbers of such tests. This, of course, helps to improve the feedback loop, which is one of the main goals of DevOps.

Performing unit tests to test some small part of the system also requires a much lower level of integration with the rest of the system. This makes writing such tests easier. The TDD methodology is designed exclusively for the use of unit tests.

Of course, anything is easier said than done, and to write most tests, even unit tests, you need to make some effort. However, if it seems that writing a unit test takes too much effort, you can test yourself by asking yourself a couple of questions:

  • Is this a modular test, or should it be considered as a test of a higher level, as a service test?
  • Is the code well-designed, is each class and method consistent with the principle of sole responsibility (is the code very tightly coupled)?

If it turns out that writing tests takes too much time and effort, then most often this indicates that the code can and probably needs to be refactored.

TDD has no advantages over conventional development


Ironically, the fact that TDD has no advantages over conventional development is said mainly by those programmers who have not even tried working in the TDD style. But until you try something, you cannot understand whether this is good or bad. Even the Coldplay band knows that "if you never try you'll never know."

TDD has two main strengths.

The first advantage of TDD is pretty obvious. If, before writing a working code, someone always writes tests for this code, then, by definition, the programmer always has a self-testing code at his disposal. Here is what Martin Fowler writes about this: “You have a self-testing code if you can run a sequence of automated tests on a code base and, upon successful completion of the tests, you can be sure that the code is free from significant defects.”

In other words, using TDD means that the code works exactly as the programmer needs.

This is a wonderful fact, since it gives serious confidence in the stability of the code to any adverse change. The use of TDD allows the programmer to instantly find out if something in the code base has deteriorated as a result of refactoring or extension of the code. Better still, it lets you know if there are any errors in the system.

Self-testing code allows development teams to truly take advantage of the benefits of continuous integration and code deployment systems .

The second main advantage of TDD is that this development methodology makes programmers, even before writing working code, think about what they are going to write and how they are going to write it.

Thinking over the code before writing it leads to the fact that the developer is really immersed in the essence of the needs of the business and takes into account borderline cases and possible difficulties in advance when implementing the appropriate mechanisms. As a result, when writing working code, the effort is spent on exactly what you need. In addition, the use of TDD leads to the fact that developers are planning a future device systems in terms of their structure and architecture. If such things are planned and taken into account from the very beginning of work on the system, this will seriously improve its ability to scale and expand.

TDD is slow


Developers who say that TDD is slow, usually those who have worked using this methodology for a while, and then returned to writing tests that were executed after the code was created. In general, this is the reason that is most often referred to when speaking about why someone does not use TDD.

I can also understand why programmers think so. Thinking about code before writing it takes time. Time is spent on creating tests for everything that is planned to be implemented, even for methods for which, under normal conditions, tests might not have been written. Finding ways to effectively use stubs and mobs can also sometimes not be the fastest and most enjoyable task.

The situation is also aggravated by the fact that the productivity and efficiency of programmers are often evaluated in terms of the speed of their work and the time spent on creating the finished product. All this pushes the team of programmers to the desire to work as quickly as possible, to the desire to fit into the tight schedules of work.

In this paper the system of the four indicators for assessing the effectiveness of DevOps:

  1. Deployment Frequency.
  2. Change execution time.
  3. Service recovery time.
  4. The frequency of failures with changes.

I am particularly interested in the indicators “Deployment Frequency” and “Failure Frequency with Changes”.

When using TDD, as I said, based on the assumption that the system was built exclusively using TDD, the development team, immediately after making a change to the system, will know if this change did not violate the performance of any from parts of the system. The use of TDD, in addition, increases the speed of finding errors. The fact that the tests are designed to test very small fragments of code allows you to very quickly find out where the project failed. This leads to the fact that errors are less frequently detected in an already deployed updated version of the system, which reduces the rate of “Failure rate during changes”.

The fact that the development team will have a high level of confidence in the sustainability of the project and in its correct work means that the team can decide whether to deploy the project when the tests are completed successfully. Has anyone done project deployment on demand?

The Phoenix Project covers four types of work. One of them is Unexpected Work. Such work has to be done when developers need to take their mind off what they are doing and do something else. Usually a correction of some kind of mistake. If the project is built from self-testing code, this minimizes the amount of errors. This, in turn, leads to minimization of the amount of “unexpected work”. And the less such “surprises” in the developer’s life - the better he feels and the more productive and better he works.

If developers can worry less about bugs in production, this means that they can devote more time to developing new useful functionality of the product. And if developers think over the code in advance, apply useful principles like SOLID and constantly engage in refactoring, this minimizes the amount of “technical debt”. When a team is confident in the functionality and quality of the code, it can literally deploy it in two counts. All this increases the speed of work.

Summary


Like any other tool, like any other development approach, the TDD methodology may seem awkward at first. And before programmers properly master this methodology, it may seem a little slow to them. What can I say? To quote Jez Humble: “If something causes discomfort, do it more often and you can handle it.”

And now I suggest you practice the TDD methodology and do it until the TDD ceases to cause you discomfort :)

Dear readers! Do you use the TDD methodology when working on your projects?

Source: https://habr.com/ru/post/undefined/


All Articles