So far, the outer circle and the middle circle of the circle of life have been introduced, and now it is the technical practice of the inner circle, which is also the most critical practice of Agile. Whether the technical practice can be effectively implemented is related to the success of the outer practice, which can be said to be the most important support of Agile.

Technical practices require developers to do a lot of minute, even second, deep, ritualistic behavior. So much so that most teams try to remove these practices, but remove them and you’ll see what pseudo-agile is. These technical practices are at the heart of agile. Agile without test-driven development, refactoring, simple design, and pair programming is doomed to failure.

To be clear, each of these technical practices could write a book on its own. So it’s not going to be detailed, it’s going to be sketchy, but if you want to know what’s going to happen, go straight to the classics. Let’s start with test-driven development.

Test-driven development

What are the responsibilities of a programmer? Obvious. – Write a program. What does a program consist of? Yes, the code. A line of code constitutes a code file, a code file constitutes a program, these codes can not go wrong, even if a character error, may also lead to completely opposite results, causing immeasurable losses to the user. So how can programmers make sure they write the right code?

Let’s start with another profession that’s been around for a long time, accounting. An accountant’s job is much like that of a programmer. He or she has to keep a ledger of information in line after line without making any mistakes.

They invented double entry bookkeeping 1,000 years ago. In simple terms, each transaction is written to the book twice – a credit in one set of accounts and a debit in the other. The two accounts end up in the balance sheet, and the difference is zero. If it’s not zero, it’s probably an error.

Don’t hit me, accountant. It’s a simplification

From the beginning, accountants were required to record transactions one by one and balance balances immediately after each transaction was recorded. The advantage of doing this is that errors can be found immediately, and locating errors can be complicated if multiple transactions are recorded and balances are balanced.

Test-driven development is a practice that is the same practice in the world of programmers. It requires programmers to add one behavior at a time, write a test that fails, and then produce code that happens to pass that test. This can detect errors immediately. Similarly, if you write a lot of production code and then test it, it’s hard to find problems with your code.

The purpose of both practices is to avoid errors in an important text.

TDD three principles

The rules of TDD are simple and can be summarized as follows:

  • Write a test that fails due to lack of production code, and then write production code.
  • Only one test is allowed to be written that just fails – compilation failures count as failures.
  • You are allowed to write just enough production code to pass the current failing test.

Does it look stupid? Stupid, yes, I thought this rule was stupid when I first encountered TDD. In order to write that one plus one equals two, I must first assert that one plus one really equals two before I begin to write the real one plus one.

If a programmer strictly follows the rules of three, what is his working state?

Write a test for nonexistent production code first, because the test calls nonexistent elements and the compilation fails. After this element is filled in in production code, the test passes. Go back to the test file and write tests… This means that programmers work in cycles of minutes or even seconds, switching back and forth between testing code and production code in extremely short periods of time.

This means that you can’t write an entire function, or even an entire if statement, all at once. Because the tests need to fail just right, the code needs to pass just right. Most programmers find these three rules so disruptive of their code that they can’t tolerate a rule that seems ridiculous.

So, is there any real benefit?

debugging

Strict adherence to the three principles of TDD means that any programmer can write code that always works a minute ago. What does that mean? This means that every error you encounter now occurs in less than a minute. Debugging an error that occurs in less than a minute is not a problem for a programmer. You don’t even have to use the debugger to figure out what went wrong in your head.

Test-driven development programmers are bad at using debuggers because they don’t use them very often, they often deal with tests they’ve just written. So is the code written this way always bug-free and doesn’t need debugging at all?

Of course not. The purpose of TDD is to make your code better, but there is no guarantee that your code will be bug-free, but TDD greatly reduces the incidence and severity of bugs.

The document

How many programmers struggle with writing documentation? When do programmers like documents the most? Integrating/taking over someone else’s code base, of course. But even with integration, are you going to scrutinize that long, stinky PDF file? I’m not going to jump right to the code examples, because documentation is deceptive, and code isn’t.

If you follow the three rules, every test you write is a code example of how to call an API, how to create an object. The tests have covered more than 90% of usage scenarios.

** The tests you write become documentation of the system under test. ** It is written in a familiar language, runs, and is always in sync with production code. The document is perfect because it is the code itself.

Moreover, the tests themselves do not combine into a system. These tests do not know each other and do not depend on each other. Each test is a small, independent unit of code that describes the way a small part of the system behaves.

completeness

Looking at this, one might say, well, can I stop TDD and write the production code first, and then make up the tests later? So let’s see what happens when we do that.

First, you’ve written all the production code, now you start testing, and if nothing else, all the tests you’ve written will pass. Everything is fine until you run into a function that’s not very easy to test because you didn’t write the tests first, and you didn’t think about testability.

Now you need to change the production code first, and then make up for the test that is not easy to write. The question is, how can you make sure that the production code you’ve written and worked correctly doesn’t change its behavior after you make a second change? And to make it easy to test, you might want to break the coupling, add abstractions, add functions… It’s so annoying when it’s working!

Okay, bad luck, tight schedule, heavy task, it’s too boring to change, but it works now, let’s hang up, then make up the other tests.

Test completed, run a test, all over, happy, cross!

Your test is incomplete! All the tests passed here give only one piece of information: the functionality under test was not broken. So what about features that aren’t being tested? Are you confident that my tests are complete and my code is ready to deploy?

If you follow the three principles of TDD, it means that every line of your production code is guaranteed to be tested – first there are tests, then there are lines of production code that just happen to pass. Your tests are complete, you have the confidence to deploy the code you tested, and these tests tell us that our system is reliable and deployable.

But is this code 100% covered? Obviously not, and even the rule of three doesn’t apply in some cases (read the books for more), which means that even if you’re strict with TDD, you’re unlikely to write 100% coverage. But it still gives you more than 90% coverage, which is good enough for the rest of our deployment.

fun

Again, since you mentioned test completeness, I can use the test coverage tool to track my test coverage and work my way up.

If you had, you probably wouldn’t have said it so optimistically. Not to mention how difficult it is, it’s a very boring process. The code is already working, and you’re still thinking about scenarios and making up tests for scenarios. If you don’t believe me, you can see how boring it is. Not to mention all the code you’ll encounter along the way that you can’t write tests for.

design

What is TDD in full? Yes, test-driven Development. But in the company, I heard another name of test-driven Design. The design here could be a long one if we expand it, but we’ll just talk about the testability of the code.

So let’s go back and think about why we mentioned before that there was untestable code in the aftertest. Because it’s coupled to other behaviors, you don’t design them into code that’s easy to test. Testability should be the least likely word to come to mind if you’re testing after the fact.

If you write tests first, your code is unlikely to have untestable code. This forces you to design your production code to be easy to test. How easy to test? Yes, decoupled, your code is going to be poorly coupled code, and TDD forces you to write highly decoupled code.

The courage to

The benefits of TDD are mentioned above, but ultimately what we need is the courage these benefits give us, the courage to change old code.

If you see bad code in your codebase, your first thought is to “clean it up”, but then you think, now it works, what if I change it and it doesn’t work, let it go. To put it bluntly, this is a kind of fear. Fear comes from insecurity, and insecurity comes from the unknown.

If everyone on the team has the same mentality, the code base will rot. Every time you add new functionality, you introduce a lot of coupling and repetition in order not to break existing functionality. So a lot of ifs and else appear, code becomes more and more unreadable, development becomes more and more slow, and managers become more and more desperate.

But if all the code comes from TDD, which means it has a complete test suite, the safety net can give us enough courage to change old code – as long as the tests don’t fail, my changes will be fine! Instead of being afraid to change code or pile up shit, TDD allows us to act like professional programmers – we have complete control over our code.

conclusion

There are always people who say that I can get the same benefits of TDD by simply writing tests and not using TDD. But the benefits of TDD go far beyond testing.

Less debugging, high quality complete documentation, high level of code protection testing, high level of decoupled code. And it gives you the courage to fix old code!

All articles were first posted on my account Teobler and my blog teobler.com