On Habré is full of adherents of TDD, and articles about developing by testing method have already surfaced. I want to deposit my five kopecks with an article about this wonderful tool.
When looking at a newbie for tests, the question immediately arises: why bother writing extra code at all? It seems that no one denies the advantages of TDD, but there are some reasons: “Yes, I heard that TDD is useful in large projects, but we have a small project”, “there are too many changes in our project, therefore tests are too big a burden for us” and etc. I will try to tell you how the unit tests help me in my work and share the experience of using.
1. Simplify code support
TDD goes hand in hand with refactoring, and this is the most obvious reason for using tests, because it is embedded in the red-green-refactoring ideology. The program code, like any system, has its life cycle, it must be maintained, changed, improved, otherwise, sooner or later it will collapse and we will reach the state of “everything is very bad, everything must be rewritten”. As a rule, this is a psychological reaction of programmers who find it so hard to understand a code that has lost a clear target and is overflowing with crutches that the consequences of the changes made may appear incomprehensible when and how. You have to either write tests (which is long, tedious and no longer corresponds to the principle “first test, then code”), reluctantly refactoring without tests (risking getting a bunch of errors), or even starting from scratch. I confess that I used to like to throw out a piece of bad code myself, but now I think that it is better to overpower myself and start refactoring without tests than throwing out practices - there will be fewer errors. If possible, write tests. The refactoring toolkit is powerful, sooner or later a piece of code will begin to take a digestible form. Do not give in to the dark side ...
I will not advise you to always write tests, because it is often possible to make a mistake in the test and skip the test. I do not see a universal method.
')
2. Clear separation of structure into modules
As a rule, the person who started using TDD already understands that modules must have well-described interfaces for writing compact tests. We change the code, input and output data are unchanged, everything is fine.
The problems here begin when the interface changes. This is what leads to the rewriting of tests, which can be a fairly large percentage of the entire code. Straws everywhere you will not podstelish, sometimes you have to change and tests. This, in turn, leads to the fact that the test code also needs to be refactored, and here someone already starts writing tests for tests and away ...
The problem is complex, I do not see a clear solution. Our brother programmer in solving this problem helps the experience and strategic vision of the system.
3. Dealing with complexity
Good people like McConnell, Brooks and others write about the main, in their opinion, software problem - complexity. The complexity grows disproportionately to the amount of code. Perhaps such a problem would not have happened if the programmer was able to load all the requirements into his head. But alas, our resources are limited. Mankind has come up with a great way to deal with this — introducing hierarchies: methods are encapsulated into classes, classes into packages, packages into libraries. From them as from bricks more difficult applications are under construction. But what to do if the description of the algorithm just breaks the brain, and he is quite a whole? Maybe I'm stupid, but some algorithms sometimes put me in a stupor: while working on one part, you completely forget the other; all code requirements simply do not fit into the brain at the same time. In such cases, the tests are my only salvation: even despite the increase in the source code (and often many times, because in a small-looking algorithm there are sometimes a large number of scenarios of behavior), time is saved due to a smaller number of regression errors in the future. Especially if the feedback error takes a long time. An important role here is played by the psychological side: using small iterations on the TDD mantra, you push yourself to deal with this hated piece of code.
However, tests are not a panacea, and many caution against the desire for 100% test coverage (what then to do with the code generated by the development environment? And other unpleasant consequences). If the code is simple, writing tests on it increases the complexity of the solution without giving anything in return, and therefore does not add value to it. How to assess the complexity? I don’t know, sometimes my inner voice tells me that it’s impossible to do without tests.
The developer’s goal is not to fanatically cover the code with 100% of tests, or exactly 5%, but to create a complex solution, balancing between development time and quality, availability of tests and their absence. In my piggy bank, projects are covered with tests from about 0 to 20%, and I do not see an unequivocal dependence on the parameters of the project: the number of people, the duration of the development or the shuffling of people in a team. Immediately I will make a reservation that the number of people in one team does not exceed three (we work as small teams), and the duration of one project does not exceed 2,000 man-hours of development.
4. Tests as specification
We have a technical task in which you can see a description of the algorithms and scenarios. What can we get from the tests? The TOR describes the desired result on the part of the end user, and the programmer describes his vision of the solution at the time of creating the application using comments, code design rules (speaking method names, etc.) and sometimes tests. Tests allow you to display the desired programmer behavior, which is useful with support when it is difficult to understand what is buried there: a clever idea, a temporary measure or just carelessness. Naturally, this is also not a panacea, nothing protects us from such a dirty trick as a “missed test”.
Now in one project I am writing tests and there my initial goal is the specification. I have a solution of ~ 100 plug-in modules, each of which is individually simple and elegant, but all together they are strung to system calls of the main system like meat on a kebab and require frequent attention: clients often change business processes or they come up with more effective ways to use. The zoo of modules has to be kept under strict control in order to quickly assess the risks of changes and development time if necessary.
5. Psychology
About the psychological side, I already wrote above, once again I want to highlight it. TDD also brings the principles of iteration to the level of work with the source code: planning a new change - making a change - finalizing the change aka red-green-refactoring. A great way to break away from the study of the algorithm / scheme / diagram and overpowering yourself to take a small step forward. Then go back again and repeat.
Normal waterfall is not so lost here: if the algorithm is placed in the head all at once, you can not iterate. If not, try other ways.
Plus, tests allow you to feel confident in the code, and help get rid of the principle of "it works - do not touch it". Any negative changes immediately show themselves.
Conclusion
All of these goals are quite close to each other, using TDD for one of them you get all these advantages, as well as disadvantages. The extra tool does not hurt anyone, the decision to use it rests on the shoulders of the developer (or the entire team). Write a good code, and may the Force be with you :)
On Habré already have: