Sunshine, zebras and great conversations
Over the last few days I’ve been in South Africa for Lets Test, held on a fantastic country retreat near Johannesburg. I facilitated a session called a ‘Testers Guide to the Illusions of Unit Testing.’ I have a confession to make. We had 2 hours to complete the workshop and, in the spirit of having too much content being way better than too little, we didn’t get to delve deeply into this question. Hence this blogpost.
We had great conversations about how unit testing is an interface between testers and developers, a gateway for deep collaboration. This is predicated on an understanding of unit testing, including what they can and can’t achieve and what relation they bear to other types of testing. One of the questions we wished to tackle was:
What is a unit of our system?
How many times have you started to test a system and asked “Hey, what are the unit tests like for this unique piece of software history?” The answer in my experience has often been, “um, yeah, about that we don’t have any, its too complex/there is no time/its going to deprecated one day.” Or alternatively you are involved in a session about the architecture of a brand new system using the latest technologies, using microservices, lambdas or whatever. In either case, wouldn’t it be useful to be able to facilitate a discussion about what is a unit of that system and what factors influence it? You might find yourself with a simpler, more observable, controllable and understandable system. Whats not to like?
Reinventing the wheel
The model itself is another wheel. Other shapes are available, although not when I model something apparently. I added a bunch of segments to the wheel that I think impact the size and shape of a unit in a systems context. The key thing here is context, I know that there are definitions of what a unit test is, contested definitions most of the time. What there isn’t a definition for what is a unit of YOUR system, the one that YOU work on. Maybe you can use the above to help your team work it out.
There are key areas here, without delving into each one individually:
- How the code is stored
- Who contributes to it
- What architecture and patterns are present
- How tightly coupled the system is
- Large – Single Database for Multiple Applications – this system may have a gigantic database bottleneck, perhaps even changes that one system makes can impact another. That’s a weighty unit.
- Medium – Broker/Queue Based – maybe messages are routed using built in routing configuration capabilities of RabbitMQ? A unit of this system involves invoking (or mocking) multiple systems so could be seen to have largeness too.
- Small – Microservice(s) – a service that does one thing well – this could be a unit in itself. At the very least, it indicates that units of your system might tend towards smallness.
Explaining with questions
The notes below include questions and examples of each of the sections. They may be useful as further prompts:
### Architectural Patterns
* What type of system architecture is employed?
* How many layers or tiers does it have?
* How many roles do components within have?
* Three tier
* Broker/queue based
### Coding Practices
* How do contributors collaborate over code?
* What design patterns are employed with the code?
* To what extent does testing drive the coding process?
* Driving Development with Tests
* Code reviews
* SOLID principles
### Concurrent Teams
* Are they internal or external teams?
* Which teams are core maintainers?
* Internal development teams
* Outsourced development teams
* Contributors to key libraries
### Source Control Strategy
* What strategy does your team employ to manage code?
* What size changes are often checked in?
* How long until changes are integrated with trunk?
* GitHub Flow
* Trunk based development
### Source Control Repo
* How large is the repository that you have?
* How many linked repositories does it have?
* How does it manage its dependencies?
### Size of Objects/Data
* What size are the important items?
* What depends on those important items?
* Core classes instantiated for every interaction
* Persistence of customer data
* Transaction and audit histories
### Risk to Whole
* What dependencies exist within your code?
* Are they any common objects or structures?
* Are there any areas teams fear to touch?
* Large common classes with multiple roles
* Old versions of dependencies
* Hard coding/configuration in the code
### Time Periods Used
* What is the lifespan of objects/data created by your code?
* Are there wide differences in the lifespan of different types of objects used?
* Long lived daemons
* Scheduled tasks
* Asynchronous HTTP requests
* Cache size and expiry rules
Over to you
Essentially, the wheel and guidance is a prompt to help answer these questions about your own system.
- What factors help to find a unit of your system? (the segments of the wheel, which can be the ones I added, or if you don’t like them I have others)
- What practices & patterns influence that unit? (your own ways of working and crafting principles)
- How practices and patterns govern size of a unit? (how the way you build stuff affects your ability to test small units of that stuff)
As with all models, this has some holes in it. Not all layers of the system will have the same size unit for example. What is a unit when you test a React application using Jest for example? What unit is creating snapshot files that creates html testing? An interesting questions plus a new and (welcome) challenge to the orthodoxy of automated/unit testing.
Trying to determine what a unit of your system is, will, at the very least lead to asking some searching questions which may provoke a reaction within your team. Overall smallness is the aim for me. After all, if asking these questions serves to help shrink a unit of your system. I believe that testers can be powerful catalysts in this regard.