Let me guess: you agree that writing tests is good. This increases the reliability of the system, speeds up development, a project with good test coverage is easy and pleasant to maintain, and TDD is generally almost the ideal development process. But not in your project. That is, it is cool, but, unfortunately, now there is so much work - just a blockage. A bunch of tasks, critical bugs alone - two dozen, plus we need to urgently finish this module and write a letter to the customer ... So, probably, we will fasten the tests at the end, if there is time left. Or in the next project. No, well, there will definitely be easier. Probably.
How did you know the situation?

So - all this nonsense. The sphere of IT is infinite, like the universe, a lot of work will always be. You can either start writing tests right now, or never do it. I have sketched out a short plan on how to start doing this in 10 steps, one step at a time, 10 minutes per step. And when I say “10 minutes,” I mean not “3 and a half hours” and not “well, some time, better is more,” namely, 600 seconds. If you do not have 600 seconds of free time per day - urgently change the project, job, profession, country of residence (underline the appropriate), because this is not life, but some kind of hard labor. Go.
')
1. Choose a test framework
Do not try to start writing your own framework from scratch - do you need it? Spending a week on choosing the optimal framework (yes, I saw such an estimate of time for this in the plans) is also silly. Here's a recipe for you: type in Google the best test framework for% language% site: stackoverflow.com. Open the first 5 links. Close those of them, where the rating of the question or the first answer is about zero. From the remaining tabs, you can safely take any recommended framework from the top three with the highest rating. With a probability of 99.5%, it will suit you. Since you have spent 3 minutes so far on this step, you can spend the remaining 7 time to go to the framework site and see examples of its use. Most likely, there everything will be simple and clear (otherwise it would not be in the top recommendations). But if suddenly there is not - choose another one by the same algorithm.
2. Writing Hello world!
Write
Hello, world! we just spit. Here, for example, in C ++.
Hello world!#include <iostream> using namespace std; int main() { cout << "Hello world!" << endl; return 0; }
And now we will do two things.
First, we will render the generation of the displayed text into separate functions. Yes, in two. This is so that they can be tested later.
Hello world! after refactoring #include <iostream> #include <string> using namespace std; string GetHello() { return "Hello"; } string GetAdressat(string adressat) { return adressat; } int main() { cout << GetHello() + " " + GetAdressat("world") + "!" << endl; return 0; }
Secondly, we will carry out the written functions somewhere from this file. Depending on the approach and the language used, these can be just separate code files or a library. This is necessary in order to later call these functions from tests.
We will have this:
HelloFunctions.h #include <string> using namespace std; string GetHello(); string GetAdressat(string adressat);
HelloFunctions.cpp #include "HelloFunctions.h" string GetHello() { return "Hello"; } string GetAdressat(string adressat) { return adressat; }
HelloWorld.cpp #include <iostream> #include "HelloFunctions.h" using namespace std; int main() { cout << GetHello() + " " + GetAdressat("world") + "!" << endl; return 0; }
3. Connecting the framework to Hello world!
About connecting the framework to the project is probably very well written on the framework site. Or on stackoverflow. Or on Habré. Here I, for example, once described connecting the
Google Test . Usually, it all comes down to creating a new project of a console executable application (in scripting languages ​​- a separate script), connecting the framework with an include (import \ using) pair, connecting the code under test (including the files themselves with the code or connecting the library) - well, everything . If you do not believe that this step can be done in 10 minutes - open Youtube, write in the search the name of your framework and watch 20 videos of approximately the same content, which prove this.
4. We understand the possibilities of the framework
First we need to find out:
- How to write one unit test
- How to run unit tests
These questions are usually answered by all the same articles I mentioned above. Do not climb right into the jungle of the framework. Start with simple tests that test simple things. Here, after all, as in sports, you don’t have to tear a lot of weight right away, you must first install the right technique.
Here, for example, a couple of tests for our Hello world! on the above mentioned Google Test:
#include "HelloFunctions.h" #include "gtest/gtest.h" class CHelloTest : public ::testing::Test { }; TEST_F(CHelloTest, CheckGetHello) { ASSERT_TRUE(GetHello() == "Hello"); } TEST_F(CHelloTest, GetAdressat) { ASSERT_TRUE(GetAdressat("world") == "world"); ASSERT_FALSE(GetAdressat("not world") == "world"); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
5. Connect the framework to this project
We already know how to connect the framework to the project. Remember, did in step number 3? Everything worked out. Now let's do this for a combat project. Put all the necessary framework files for yourself under SVN \ Git \ TFS \ what-you-there. Make a test project. Connect the framework to it. Include the build test project in the build process of your product. Check the build in debug and release configurations. Connect the test project, run the build on the build server. Everything should be ok. Do not load your colleagues with the appearance of a test project yet - firstly, you have not broken anything, and secondly, you have nothing to brag about either.
6. We test something simple
You remember how we carried out above from
Hello world! part of the functionality in external code? Pay attention to how these functions have turned out: they do not depend on global variables, on the state of any objects, or on external data from files or databases. The result depends only on the arguments passed. Find something similar in your project. Surely because you have any functions for converting something somewhere, serialization / de-serialization, packaging / unpacking, encryption / decryption, etc. Do not think yet about how necessary and useful functionality you are testing. Your task is to write a simple test, but for a combat project. Run, see "1 test passed successfully."
By the way, it is at this stage that insights come to skeptics very often. Suddenly it turns out that the simplest test, for the most basic functionality, has suddenly failed. We climb into the code - and suddenly we find something like
return 12;
And it turns out that the main code just called this function for now with such arguments that everything was ok, but at any moment this could change.
7. We test something more difficult
You already know how to test simple things. Now figure out how to test something that has external dependencies. See how your framework offers to do preparation for the start of the test and cleaning after it. Understand what
moki and stubs . Think about how to test some of your code that reads data from a file or from a database. Is it easy to replace the input source? Maybe it is worth slightly changing the code to make it easier? Do it if necessary. Write a test for this code.
8. We write a test for a bug
How does your work on a bug usually look like? You take it from the bugtracker, try to reproduce it if it does not work - return the tester, if it works, debug it to understand its location, find a piece of code with an error, correct it, test it, give it to the tester. Fine. And now try to work on the next bug between the steps “find a mistake” and “fix” to add one more step - to write a test for this error. Such that it fell for the current code. This is a huge buzz, fix the code - and don’t bother to test it with your hands, but run the test that fell a few minutes ago and see “successfully” at its output. In addition to this aesthetic pleasure, this test can be given to the tester and used in the future for regression testing (and also for testing the side branches of the product, the “in the field” project, etc.). Of course, not everything and it is not always possible to test it this way, it can be difficult with UI, with cross-browser compatibility, with multithreading. Do not bother if writing a test takes you many, many hours. In the end, this technology is designed to make your life easier, and not to make you dance to your tune.
9. First TDD
How does your work usually look like when developing a new functional? Probably you think first. Then you design what you will do - you sketch the names of interfaces, classes, then the names of the methods, fill them with implementation, start, debug. Great, almost nothing needs to be changed. Just at that moment when you already have interfaces, classes and method names, but there is no implementation yet - write tests for them. Simple ones called the method checked the result. Pay attention, as already at this stage you will notice the illogicality of some names, the lack or excessiveness of arguments in the methods, unnecessary or missing dependencies, etc. At the same time, for the time being, this is almost worthless (the implementation has not been written yet) . We corrected the architecture, added tests, launched - we saw a bunch of failed tests. Great, that's the way it should be. They wrote the implementation, started the tests - saw most of them passed, fixed errors, achieved successful completion of all tests - great, it's done. You feel how good it became, what moral satisfaction did you get? It is slightly reminiscent of the pleasure of getting some achivki in the game. And why? Because it can be measured! “The code passes 18 tests with a test coverage of 90%” - this sounds cooler, much cooler than “well, the feature seems to be implemented, I so poked a little, it seems, does not fall.” It gives the right to be proud. You go home - and you clearly understand that something has done something useful in a day, this “something” is measurable, tangible, real.
10. We fasten the test run to the CI-server.
There is little point in tests if you don’t run them. Run them manually - long and pointless. Surely you have a build server with some TeamCity or CruiseControl where your product is going. So, most good build servers immediately, out of the box, support the launch of tests and even parse their logs and draw beautiful reports. Compliance here, of course, is not “everything is compatible with everyone,” but if you took the test framework according to the advice at the beginning of the article, the chances that everything will work are very high. For example, the TeamCity and Google Test mentioned by me are perfectly friendly.
Afterword
A meticulous reader may notice that the items starting from somewhere from the seventh to the eighth will most likely not fit into the stated in the title “10 minutes per step”. What can I say? Consider that I, the bad person, have slightly pinned you. However, if you passed these points with righteous indignation in practice, then:
- You already have a project to which tests are bolted. They are running, working, they are more than zero and they are already benefiting you.
- You have experience in this whole business.
- The second time you get seriously faster.
So decide whether it was worth it or not.
Somewhere after the 8th is a good time to submit a test project to your team. Explain in 2-3 paragraphs what and how, show a simple example of the test, notice that, they say, “feel free to add your own tests”, but don’t push too much. If you did not write tests, most likely the first impression will be cautious skepticism and misunderstanding. This is quickly treated after the second or third mention at the meeting that, they say, "we found this bug thanks to the test" or "and here the test was written and we immediately find out if it breaks again." Programmers are a rational people, they will understand and catch up.