JavaRush /Java Blog /Random EN /What is TDD and unit testing [translation]
Dr-John Zoidberg
Level 41
Марс

What is TDD and unit testing [translation]

Published in the Random EN group
This article is an adaptation of a chapter from the book The Complete Software Career Guide. Its author, John Sonmez, writes it and posts some chapters on his website.
What is TDD and unit testing [translation] - 1

A short glossary for beginners

Unit testing or unit testing is a process in programming that allows you to check individual modules of a program’s source code for correctness. The idea is to write tests for every non-trivial function or method. Regression testing is a general name for all types of software testing aimed at detecting errors in already tested areas of the source code. Such errors - when, after making changes to the program, something that should continue to work stops working - are called regression errors. Red result, fail - failure of the test. The difference between the expected result and the actual one. Green result, pass - positive test result. The actual result is no different from what was obtained. ***
What is TDD and unit testing [translation] - 2
I have a very mixed relationship with Test Driven Development (TDD) and unit testing, going from love to hate and back again. I was an avid fan and at the same time a suspicious skeptic about the use of this, and other, “best practices.” The reason for my attitude is based on the fact that a serious problem has emerged in software development processes: developers, and sometimes managers, use certain tools and methodologies only because they belong to “best practices”. The real reason for their use remains unclear. One day I started working on a certain project, and in the process I was informed that we would be modifying code covered by a huge number of unit tests. No joke, there were about 3000 of them. This is usually a good sign, a signal that the developers are using advanced methodologies. The code with this approach is most often structured, and it is based on a well-thought-out architecture. In a word, the presence of tests made me happy, if only because it meant making my job as a mentor of programmers easier. Since we already had unit tests, all I had to do was connect the development team to support them and start writing our own code. I opened the IDE (integrated development environment) and loaded the project.
What is TDD and unit testing [translation] - 3
It was a big project! I found a folder labeled "unit tests". “Great,” I thought. - Let's launch it and see what happens. It only took a few minutes, and to my surprise, all the tests passed, everything was green ( "green" is a positive result of the test. Signals that the code is working as expected. Red indicates "failure" or fail, then there is a case when the code does not work correctly - translator's note ). They all passed the test. At that moment, the skeptic in me woke up. How come, three thousand unit tests, and they took them all at once - and gave a positive result? In my long practice, I could not remember a time when I started working on a project without a single negative unit test in the code. What to do? Check manually! ChY chose one random test, not the most revealing, but it was immediately clear what he was checking. But while working through it, I noticed something absurd: the test did not contain any comparisons with the expected result (asserts)! That is, in reality nothing was checked at all ! There were certain steps in the test, they were carried out, but at the end of the test, where he should compare the actual and expected results, there was no check. The "test" did not test anything. I opened another test. Even better: the comparison operator with the result has been commented out. Brilliantly! This is a great way to do a test pass, just comment out the code that is causing it to fail. I checked another test, then another... None of them checked anything. Three thousand tests, and all of them are completely useless. There is a huge difference between writing unit tests and understanding unit testing and test-driven development (TDD).

What is unit testing?

What is TDD and unit testing [translation] - 4
The basic idea of ​​unit testing is to write tests that test the smallest “unit” of code. Unit tests are usually written in the same programming language as the application's source code. They are created directly to test this code. That is, unit tests are code that checks the correctness of other code. I use the word “test” quite liberally in the context, because unit tests in some sense are not tests. They don't experience anything. What I mean is that when you run a unit test you don't usually find that some code doesn't work. You discover this while writing the test, because you will change the code until the test turns green. Yes, the code may change later and then your test may fail. So in this sense, a unit test is a regression test. A unit test is not like a normal test where you have a few steps that you are going to follow and you see whether the software works correctly or not. During the process of writing a unit test, you discover whether the code does what it is supposed to do or not, and you will change the code until the test passes.
What is TDD and unit testing [translation] - 5
Why not write a unit test and check if it passes? If you think about it this way, then unit tests turn into some kind of absolute requirements for certain code modules at a very low level. You can think of a unit test as an absolute specification . A unit test determines that under these conditions, with this particular set of inputs, there is an output that you should get from this unit of code. True unit testing identifies the smallest coherent unit of code, which in most programming languages ​​- at least object-oriented ones - is a class.

What is sometimes called unit testing?

What is TDD and unit testing [translation] - 6
Unit testing is often confused with integration testing. Some "unit tests" test more than one class or test large units of code. A lot of developers claim that they write unit tests when in fact they write low-level whitebox tests. Don't argue with these guys. Just know that they actually write integration tests, and true unit tests test the smallest unit of code in isolation from other parts. Another thing that is often called unit testing is unit tests without checking against an expected value. In other words, unit tests that don't actually test anything. Any test, unitized or not, must include some kind of verification - we call it checking the actual result against the expected result. It is this reconciliation that determines whether the test passes or fails. A test that always passes is useless. A test that always fails is useless.

The Value of Unit Testing

Why am I a unit testing enthusiast? Why is it harmful to call generalized testing, which involves testing not the smallest block isolated from other code, but a larger piece of code, “unit testing”? What's the problem if some of my tests do not compare the received and expected results? At least they execute the code. I'll try to explain.
What is TDD and unit testing [translation] - 7
There are two main reasons for performing unit testing. The first is to improve the code design. Remember how I said that unit testing is not really testing? When you write proper unit tests, you force yourself to isolate the smallest unit of code. These attempts will lead you to discover problems in the structure of the code itself. You may find it very difficult to isolate the test class and not include its dependencies, and this may make you realize that your code is too tightly coupled. You may find that the core functionality you are trying to test spans multiple modules, leading you to believe that your code is not coherent enough. When you sit down to write a unit test, you may suddenly discover (and believe me, it happens!) that you have no idea what the code is supposed to do. Accordingly, you will not be able to write a unit test for it. And of course, you may find a real bug in the implementation of the code, since the unit test forces you to think outside the box and test different sets of inputs that you may not have considered.
What is TDD and unit testing [translation] - 8
If you strictly adhere to the rule of “test the smallest unit of code in isolation from the others” when creating unit tests, you are bound to find all sorts of problems with that code and the design of those modules. In the software development life cycle, unit testing is more of an evaluation activity than a testing activity. The second main goal of unit testing is to create an automated set of regression tests that can act as a low-level specification of software behavior. What does it mean? When you knead the dough, you don't break it. From this point of view, unit tests are tests, more specifically regression tests. However, the purpose of unit testing is not to simply build regression tests. In practice, unit tests rarely catch regressions, since a change to the unit of code you're testing almost always contains changes to the unit test itself. Regression testing is much more effective at a higher level, when the code is tested as a "black box", because at this level the internal structure of the code can be changed, while the external behavior is expected to remain the same. Unit tests in turn check the internal structure so that when that structure changes, the unit tests do not fail. They become unusable and now need to be changed, thrown away or rewritten. You now know more about the true purpose of unit testing than many veteran software developers.

What is Test Driven Development (TDD)?

What is TDD and unit testing [translation] - 9
In the software development process, a good specification is worth its weight in gold. The TDD approach is that before you write any code, you first write a test that will serve as a specification, that is, define what the code should do. This is an extremely powerful software development concept, but it is often misused. Typically, test-driven development means using unit tests to guide the creation of application code. But in fact, this approach can be applied at any level. However, in this article we will assume that we are using unit testing for our application. The TDD approach turns everything on its head, and instead of writing code first and then writing unit tests to test that code, you write a unit test first and then write code to make that test green. In this way, unit testing “drives” code development. This process is repeated over and over again. You write another test that defines more functionality of what the code should do. You then write and modify the code to ensure that the test completes successfully. Once you have a green result, you start refactoring the code, that is, refactoring or cleaning it up to make it more concise. This chain of processes is often called "Red-Green-Refactoring" because first the unit test fails (red), then the code is written to adapt to the test, making sure it succeeds (green), and finally the code is optimized (refactoring). .

What is the goal of TDD?

What is TDD and unit testing [translation] - 10
Test-driven development (TDD), like unit testing, can be used incorrectly. It's very easy to call what you do "TDD" and even follow the practice without understanding why you're doing it that way. The greatest value of TDD is that tests are performed to produce quality specifications. TDD is essentially the practice of writing precise specifications that can be automatically checked before coding is written. Tests are the best specifications because they don't lie. They won’t tell you after two weeks of torment with the code “that’s not what I meant at all.” Tests, when written correctly, either pass or fail. Tests clearly indicate what should happen under certain circumstances. Thus, the goal of TDD is to give us a complete understanding of what we need to implement before we start implementing it. If you're starting out with TDD and can't figure out what the test is supposed to test, then you need to ask more questions. Another important role of TDD is to preserve and optimize code. Code maintenance is expensive. I often joke that the best programmer is the one who writes the shortest code that solves some problem. Or even the one who proves that this problem does not need to be solved, and thereby completely removes the code, since it was this programmer who found the right way to reduce the number of errors and reduce the cost of maintaining the application. By using TDD, you can be absolutely sure that you are not writing any unnecessary code, since you will only be writing code to pass tests. There is a software development principle called YAGNI (you ain't going to need it). TDD prevents YAGNI.

Typical Test Driven Development (TDD) Workflow

What is TDD and unit testing [translation] - 11
Understanding the meaning of TDD from a purely academic point of view is difficult. So let's look at an example of a TDD session. Imagine sitting down at your desk and quickly sketching out what you think will be a high-level design for a feature that allows a user to log into an application and change their password if they forget it. You decide that you will start with the first implementation of the login function, creating a class that will handle all the logic for the login process. You open your favorite editor and create a unit test called "Empty login prevents user from logging in." You write unit test code that first creates an instance of the Login class (which you haven't created yet). You then write code to call a method in the Login class that passes an empty username and password. Finally, you write a check against the expected result, checking that the user with an empty login is actually not logged in. You're trying to run a test, but it doesn't even compile because you don't have a Login class. You fix this situation and create a Login class along with a method in that class to log in and another to check the user's status to see if they are logged in. So far you have not implemented the functionality of this class and the method we need. You run the test at this point. Now it compiles, but immediately fails.
What is TDD and unit testing [translation] - 12
Now you go back to the code and implement the functionality to pass the test. In our case, this means that we should get the result: “the user is not logged in.” You run the test again and now it passes. Let's move on to the next test. Now let's imagine that you need to write a test called "The user is logged in if they entered a valid username and password." You write a unit test that instantiates the Login class and attempts to log in with a username and password. In a unit test, you write a statement that the Login class should answer yes to the question of whether the user is logged in. You run this new test and of course it fails because your Login class always returns that the user is not logged in. You return to your Login class and implement some code to verify that the user is logged in. In this case, you will have to figure out how to isolate this module. For now, the easiest way to do this is to hardcode the username and password you used in your test, and if they match, then return the "user is logged in" result. You make this change, run both tests, and they both pass. Let's move on to the last step: you look at the generated code, and look for a way to reorganize and simplify it. So the TDD algorithm is:
  1. Created a test.
  2. We wrote code for this test.
  3. Refactored the code.

conclusions

What is TDD and unit testing [translation] - 13
That's all I wanted to tell you about unit testing and TDD at this stage. In fact, there are many difficulties associated with trying to isolate code modules, since the code can be very complex and confusing. Very few classes exist in complete isolation. Instead, they have dependencies, and those dependencies have dependencies, and so on. To deal with such situations, the TDD veteran uses mocks, which help isolate individual classes by replacing objects in dependent modules. This article is just an overview and somewhat simplified introduction to unit testing and TDD, we will not go into detail about dummy modules and other TDD techniques. The idea is to give you the basic concepts and principles of TDD and unit testing that hopefully you now have. Original - https://simpleprogrammer.com/2017/01/30/tdd-unit-testing/
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION