The idea to write this post came from a discussion with one of my colleagues who were interested in how writing test can make him any better at his job.
Here is my point of view on this.
1. Tests help document behaviour of the code
There is a saying: “if you don’t write tests for your code, then your code immediately becomes a Legacy”.
Imagine that you are assigned to a project where codebase is large, there is no documentation and no one to ask about how some parts of code is expected to work.
You need to make some changes.
If you fail to correctly guess the expected behaviour of some code and after you make changes code will behave not in the way it was supposed to(even if you want it this way) it can cause errors and misbehaviour of other parts of the code that depends on old behaviour. Plus there can be external systems, applications and even end users who may depend on the old behaviour of that code.
So what problems you may get into?
– compile time errors, unlikely but possible
– runtime errors, much more luckily, especially if the programming language has dynamic typing
– incorrect results. This is most likely and tricky to become aware of. Depending on whether end-user or another system check your results somehow and provide feedback in time you may become aware of the problem soon, late or even never.
After you became aware of the incorrect problem you may have trace recursively all code that is using that code you’ve changed. With big enough codebase you might decide that writing resignation latter is better than spend the rest of your life refactoring the while project.
2. Tests help write code faster
This is counterintuitive, but true.
Imagine, that you are working on the part of the code, that to check it is working you need to do many steps on frontend to reach the place where this code is executed.
For example, thank you page after successfully placed order on ecommerce site.
To test it, you need to open the site, add one or more products to cart, go to checkout page, fill in your contact data, choose shipping method and address and only then you get to that thank you page.
This may not seem worth of effort to cover with tests, because doesn’t seem to be difficult to test.
But if you need to trough this steps 20-30 times and each iteration takes at least 2 minutes then you wasted up to hour of your time!
There two options here:
– write tests tha spin up a browser to go through these steps for you and check for expected outcome (some particular text on the thank you page)
– write a test that with give input generates that thank you page and check for expected outcome.
Both options are good and if first one is called integration test, because it involves testibg multiple parts of site, and second test is unit test, because you check single unit of code (function/method).
Probably writing both of them is a good thing to do, but they help to achieve a bit different goals, which I’ll describe next.
My point here is that both tests help save you time. Computer will run these tests faster then you, they will not make a mistake (miss click or type something wrong in the form). You save time, you have results faster, you deliver working code faster. Profit.
3. Tests can be ran as part of Continuous integration pipeline.
This way if your changes break some tests, this will not go into production. Think of tests as a snapshot of expected behavior. If it changes – you will get notified where project miss behavior happened and decide if it is good or not. Then you make changes to test and all done. No surprises, but machines check that evdrything for you, faster then you can possibly do it.
4. Tests help you write more clean and maintainable code
If you are working with some framework,like Django, you would use Object Relation Mapper(ORM) to work with data, stored in database, because that is what makes frameworks so useful – this is convenient and makes you write code faster.
It is true only partially.
When you have larger codebase it becomes harder to maintain with every new line of code.
And when you need to write tests tight coupling of business logic with ORM makes it very hard to write tests.
And many developers,that are facing this complexity just don’t write tests.
Or their tests become complex and complicated themselves making them another burden to maintain.
To make testing business logic easier, you should decouple logic from framework and its ORM.
Instead of writing code that works directly with ORM objects, write code that works with languages’ basic data objects.
Instead of writing code that calculates total order price using Customer, Order and Product models, write function that as input takes only data that it needs to produce needed result:
from Customer model – customer discount percent as Decimal or even integer
For each product – price as Decimal and available quantity as integer
All product wrap into the list
And so on.
This way your function, that calculates orders’ total cost will not depend on database.
This way you can write test for a pure function that expects several variables of basic types as arguments and returns total cost as Decimal as return value.
Such function doesn’t require a database to run and it’s result doesn’t depend on data persisted in database, this way reducing complexity.
Such function always returns same result for same set of arguments.
For such function it is very easy to write tests, because it is just a function that needs very simple test data.
And it runs faster.
You can have separate test suite for business logic and it will work faster because it doesn’t need to setup database and depend on any external system at all.
So if one day you need to only change business logic – you know where to go to make changes to code and to tests, you understand what are dependencies (ideally – none). And you have happy time maintaining this project, even after a long time without touching it.
That were my ideas that should motivate developers to write tests.