In the last days I’m facing a huge question: With several teams working with several projects, how to avoid new bugs?
In the company that I work, we have a lot of microservices, but there is no owner. Probably someone can start working in a project and a new feature should be added in a microservice. When this happen, no bug should be added to any service in any client!
In this post I will describe how we are handling features changes with TDD.
TDD process review
When using TDD to develop any software we must:
- Configure Testing Frameworks
- Write a Test that fails
- Write the code that fixes all the Tests
- Refactor your code if necessary
This is what we call Red-Green-Refactor. First you make a test fail, then you fix and at least you improve your code organization.
Some thoughts on Red-Green-Refactor
Red-Green-Refactor most of time is used for Unit Tests. And this lead some questions about requirements:
- How can we ensure that all the requirements are implemented by the our code?
- How can we ensure that any future implementation will not change any requirement?
- How can we ensure that all developers will know all requirements in any future implementation?
If we are talking about Unit Testing, this questions is not applied, because we are testing a class or a small piece of code. One change on this piece of code can change all software behaviour.
Create Test for Requirements
For all types of tests, none apply for requirements, they are all named according with the implementation components:
- Unit testing
- Integration testing
- System testing
- Sanity testing
- Smoke testing
- Interface testing
- Regression testing
- Beta/Acceptance testing
But… This names can mean nothing, we can create test for any purpose we want! So, I’m proposing a new kind of test: Requirement Test… Ok, it already exists! No problem.
- First create A Test Suite based on the Domain, not on the implementation. Instead of
UserManagerTests
, just aUserTests
class. - Then add all Requirement with a plain and complete text descriptions. This works fine on JUnit5
- If any test are failing, correct the code
class UserTests {
@Test
@DisplayName("It SHOULD allow create Users without a name")
void createWithoutNameTest() {
// Do the tests
fail();
}
@Test
@DisplayName("It SHOULD only requires username and password")
void createWithMinimumPropertiesTest() {
// Do the tests
fail();
}
}
It is important to keep this tests in all changes, because this tests keep a memory of all requirements.
Tips
- The more you abuse of words like SHOULD, NEVER, ALWAYS or CAN better is your description.
- Keep in mind the desired behaviour should be expressed as a Test and a Test Description. Any future development should know why all theses tests exists.
- Keep in mind that in a future modification, the developer cannot talk with you, but he can see what you were thinking in the moment of the change.
Conclusion
Most of TDD approaches always talk about unit testing, but unit testing does not prevent you of any undesired requirement change. For that we purpose a Requirement Test. All requirement should be written as a Test. If any failed it cab be:
- The requirement has changed
- This change adds a bug in any other piece of code.