TDD Reloaded
Test driven development is a beast to tame. I like TDD but in the past it was quite difficult to achive, at least in a pure-consultant approach with a "turn key" framework behind.
There are two major concept often not grasped correctly.
First: What is the reason to use TDD?
TDD main purpose is to increase code quality, to be able to refactor without fear and to reduce regressions.
Refactoring code is a cornerstone on evolving architecture.
Microservices needs refactoring: if you plan to split or merge microservices (which is indeed one of the open options in the micro-world) you need to be able to guarantee your API remains stable.
In 2010 the architecture was written in stone, and changing it often was a huge problem. You have more luck to propose to your Customer to do an holy rewrite.
Banks and Telco rewrites legacy "often".
Insurance sector keeps legacy code around for decades.
The key rule of TDD is
Writing the tests first: The tests should be written before the functionality that is to be tested.
Obviously it is very difficult to work in this way, at least in my humble experience.
Lets face it: it costs a lot to write code in TDD, and how wikipedia observe
Management support is essential. Without the entire organization believing that test-driven development is going to improve the product, management may feel that time spent writing tests is wasted
So you end up writing your code, adding tests as "last thing" and the whole TDD becomes "annoying", also because test writing is the first thing to throw out of the window when there is lack of time.
In this article I will provide some suggestions on how to reduce the costs and improve the "willingness" to do tests, especially focusing on Java.
Java tools rulez
If you live in your happy SpringBoot garden, you can have a good setup installing the following libraries:
- Unit Tests increase code base size.
- JUnit 5 offers goodies like parallel test executions.
It seems worthless, until you hit more than 700 unit tests (mostly are boring parsing of fixed length stuff but you need them, trust me)
This is an example of a junit-platform.properties file you can use to speed up tests:# To enable parallel test execution de-comment the following
# Configuration parameters to execute top-level classes in parallel but methods in same thread
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default =same_thread
junit.jupiter.execution.parallel.mode.classes.default =concurrent
# Computes the desired parallelism based on the number of available processors/cores multiplied by the junit.jupiter.execution.parallel.config.dynamic.factor configuration parameter (defaults to 1).
# The optional junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor configuration parameter can be used to limit the maximum number of threads.
junit.jupiter.execution.parallel.config.strategy =dynamic
# Factor to be multiplied by the number of available processors/cores to determine the desired parallelism for the dynamic configuration strategy
junit.jupiter.execution.parallel.config.dynamic.factor=2.5 - Also, JUnit 5 offer facility to create parametrized tests reading inputs from fancy comma separated files (CSV) and this can be a life saver, if you have the lucky to obtain huge excels from business with test cases (sometimes it happens :)
- JUnit 5 offers goodies like parallel test executions.
- Spring Boot Test library support like
- Spring Security integration (for instance for setting up the roles for the security)
- @Sql annotations inside the spring-test library. Very handy to setup some data in the database before starting a test
- Mockito mocking library: it has a fantastic name & it is also MIT Licensed (difficult to see these two things together).
With mockito you can easily "emulate" (mock) the response from the external integrated services, and do proper unit test. You can even mock your database API and also the internal "clock" of the JVM - H2 on memory Rulez!
Testing database functionality is complex. Normally you will end up mocking it. But for bug fixing I was forced to copy some data on the bugged use case and to fix it I needed to work on SQL-level.
I have a nice time converting postgresql data in a set of sql 92 files which could be understood by H2, all with Hibernate and the proper dialect.
I discovered H2 support a limited subset of PostgreSql/DB2/Oracle syntax to ease this scenario. In my humble opinion, it was easier to avoid it (!) if you use hibernate.