Often, when I speak with OutSystems developers about Unit Testing, TDD, BDD, I get some interest but also a lot of pushback. “It’s too much work”, “It’s not worth it”, “For a 3 month project? Are you nuts?”, or even “How many extra sprints will it take?”
I believe the reason for this pushback is because developing on the OutSystems platform is so efficient when compared to other technologies that it tends to obfuscate the value of testing.
There are a few misconceptions about TDD so let’s try to set the score straight.
TDD Is About Development
Test Driven Development is a development process. It is not about testing.
My point is, the tests you get by using TDD are a bonus, the most important factor is the development process.
In practice, you start by developing the tests first. But what does this mean?
In a traditional object oriented language you would code using classes. These classes interact with other pieces of software through their public interfaces (methods and properties).
You start by thinking about the behaviour of your class and how to interact with it, designing the public interfaces necessary to support your use case. What TDD adds here is, instead of doing this on a piece of paper or on your head, do this exercise while designing the tests to call your class/unit/module.
In OutSystems the closest we have to a class is a module which, interestingly, is closer to the TDD concepts. It does implement a behaviour and it does have a public interface (blocks, methods, structures, etc).
In this sense you can do the same exercise, start by creating a test that reflects the desired behaviour of your module and what would the public interfaces look like.
At this point the implementation details are irrelevant.
TDD Is About Your Code Behaviour
I’ve seen automated tests done in OutSystems with extreme levels of detail, trying to capture how well the code is implemented. So much in detail, that the developer would be able to tell exactly where, on the implementation the code is failing, just by looking at the failed assertions.
Use TDD to test the behaviour of your code, not debug it, you have better tools for that.
TDD Is About Constant Feedback on Your Code
At this stage of development we may not have users to provide us with feedback. Consider TDD as your constant source of feedback about your code.
Just don’t get upset like you do with users. After all, you wrote the tests yourself.
Traditional Development vs TDD
So, how do we normally start a project in OutSystems?
You create a new application, new module and eventually some data sources.
You create a rudimentary page because you need to test what you’ll be implementing.
Using TDD you would create an application with a new module and eventually some data sources, then another application with a test module.
You create a rudimentary test page because you need to test what you’ll be implementing.
So far it is incredibly similar.
Fast forward a few hours you have several methods which you tested and a defined behaviour of your module.
So what are the differences?
Tell us, which one do you think is more likely to deliver better quality software?
Evolving Your Code on TDD
After a while, you got requests to add features or you got some feedback from the users or maybe, however unlikely, a bug was found in your code.
Where would you naturally start? The tests of course.
New features always mean new behaviours, bugs mean your app is not behaving properly, feedback means one or the other or both.
Any changes in behaviour should always start in the tests. This way you’ll always have the behaviour documented.
You also won’t have to go through the tedious process of manually testing everything again.
If you break the tests you’ll have a pretty good sense that you’re breaking all other consumers.
The TDD Process
This methodology imposes a certain discipline in your work. It creates a rhythm to development and augments your focus to the different aspects of what you’re coding.
Red - Write a Failing Test
You start development by coding the test:
- Consider what you need (the behaviour)
- And how would it be used (the public interfaces)
At this stage, you are already doing a design exercise, you are coding, you are deciding how your method will be used by the world around it. You just happen to create a test for it.
Green - Make the Test Pass
At this stage you’re actually solving the problem, this is your only concern.
Implement the code as simply as you can, if you didn’t get the interface right at the first time, it is ok to go back and redefine things.
Continue evolving your code so that the test passes.
Blue - Refactor Your Code
Refactor your code for efficiency and elegance.
Look for patterns and code smells, implement best practices, eliminate duplicate code.
Bring architecture into focus, should I extract this internal method to another module?
If you need to change the public interfaces of this particular method or you need to implement more functionalities, go back to step 1.
TDD vs Unit Testing
Although TDD is Unit Testing, typically when we reference unit testing you develop your tests after you develop your code.
Your goal is coverage so that every method (public or private) has the expected outputs.
You’re testing the implementation rather than the behaviour.
TDD vs BDD
This one is easy. Behaviour Driven Development is TDD done right.
BDD was originally conceived as a better way to teach TDD.
It implements 2 extra concepts.
An ubiquitous language, a domain specific language for the project (popularised by Eric Evans Domain Driven Design).
Specifications expressed in this common language provide a better understanding for everyone involved with the project of the desired behaviours.
Specifications also express the behaviours by example which is easier to translate to code and also provide us with the acceptance criteria.
It extends the development process outside of the development team. The specifications should be done in collaboration with the business side.
This is easier said than done and reminds me a lot of the painfull path Agile Methodologies had. Agile was easily adopted by the development teams but very hard to be adopted by the organization. Teams around development just couldn't keep up.
There are plenty of studies demonstrating the higher quality output of developers using TDD.
TDD is a great way to create code. It imposes a certain discipline in the way you work.
Folks often question if it is worth it when compared with the added time it takes to create the tests, however much of the time used to build the test is actually invested in thinking about your public interfaces and what behaviour you want from your code.
This planning will determine how your piece of code interacts with the universe and a bad implementation here can have significant repercussions on the rest of the project.
Much efficiency and consistency is also gained by having the test being executed automatically vs manually.
Future iterations of development, bug fixes, new features (and new bugs) also benefit from using TDD.
With TDD you will always have a very good understanding of what is the expected behaviour of the code especially when handing it to other developers.
All in all the relative investment in effort when using TDD is by far offset with the gains in software quality.
Frameworks Available on the Forge
There are a few frameworks available on the OutSystems Forge. We recommend the BDD Framework but there are plenty out there to choose from.
Go check them out.
Have fun a go build those apps using TDD/BDD.