This is the fourth in a series about unit testing. See the links at the bottom of this blog for the previous iterations. In this blog, I want to take a closer look at the eternal debate surrounding unit testing. This debate is not limited to low-code, but to keep the scope more limited, I will purely focus on my own experiences with Mendix low-code projects and the teams working on those projects.
When I bring up the topic of unit testing, I usually get the same questions/arguments mentioned.
A prominent one is that I have never used it and do not see any benefits. At its core, this statement seems to indicate a fear of something new or even a reluctance to change the development process to improve quality. This opinion can be changed, assuming a shared goal is to enhance the quality while maintaining low-code development speed.
As you can see, unit testing is the foundation of the testing pyramid. As the layers of complexity increase for a Mendix application, it becomes increasingly difficult to test everything correctly, with each release gaining test cycles or simply not testing everything that might be crucial. Most Mendix applications are tested by utilizing manual and exploratory testing and UI and API testing, the two top levels of the pyramid. When Mendix applications grow, the UI & API testing is even automated. Testing layers that are 4+ sub-microflows deep and require a ton of data input in pages are complex to test from the UI and even more challenging to scale to multiple scenarios.
For this unit testing could be a blessing. Ensure that at the bottom of your microflow spaghetti, there are single-purpose sub-microflows that take care of the specific parts of your functionality. Applying unit tests for each of these sub-microflows will add some maintenance, but it should allow you to remove some UI tests that are essentially not scalable and only test the tip of the pyramid.
It is crucial to make sure that the microflows tested are single-purpose. Having giant microflows with a lot of complex logic makes them unsuitable for unit testing. In that case, every bit or requirement change will cause you the same headache as the UI testing approach.
The benefits of utilizing unit tests to answer the doubts are that you can test the functionality at a lower level, thereby testing multiple scenarios without an overly complex set of input parameters, and when the UI changes or one unit changes, you do not have to change all the tests, just the ones for that specific unit.
This is a statement I generally disagree with. If your project is meant to go into production, someone will have to maintain it, and/or continuous development will happen for this project. Unit tests will then help both you during development and your fellow developers after you (even you in six months) to test the code you thought you did not change and the updated/newly added code.
When you test functionality that has some level of logic in it and does not just open a new page or create a simple object, you should create unit tests for it. After all, if you tested it once or twice manually, the unit test could probably have been built, and it would have saved you time from that moment forward. Of course, if the functionality tested changes, you probably would need to update the unit test, but an update is not the same effort as an initial creation most of the time. With the adjusted module I described in a previous blog iteration, the templates will help you fill in the blanks more easily.
And remember, when you are in a pinch due to production being on fire, you want to release as quickly as possible after that bug fix. Would you like to wait for your test engineer to go over everything? Are you sure you tested everything? With unit tests already there, the bulk of the work could be tested with a push of a button. So, even small projects benefit from the implementation of unit testing.
As a bonus, your unit tests provide a layer of context to your code's functionality. Knowing which input parameters should result in which output helps anyone working on this project after you interpret the code. This, along with annotations, documentation, and user stories, could save someone a lot of time.
Implementing unit tests increases the quality of the code. As mentioned before, when you apply unit tests, you need to have microflows that have a single purpose; otherwise, the unit tests trying to validate the functionality of those microflows will become too complex. And what is one of the best practices in Mendix? Keep your microflows small and reusable.
Your code should be highly suitable for unit testing, especially if you have focused on properly separating modules and functions. Even in this situation, automating your testing via pure UI testing or only applying manual testing would not be scalable. I do not see adding unit tests as unnecessary complexity. Yes, using the unit testing module (even the enhanced version) adds additional microflows, which means additional code is needed for your project. (if only Mendix had the option to separate unit test microflow in their own document type). But it also gives context to that code. It allows you to test the code with a push of a button or as part of a CI/CD (Continuous Integration / Continuous Deployment) pipeline. It helps your UI testing team focus on the UI elements of your application.
While you should be so lucky that your project team has dedicated test engineers, this statement is not entirely true or false. Every developer should already validate that their code functions. And I am not just talking about the happy path. Test engineers are generally wired slightly differently from developers and, therefore, have the ability to think of even more scenarios on how to validate the code. A test engineer with an affinity for Mendix is even more valuable on the team. Likewise, it is for a Mendix developer with an affinity for testing. As a team, you should determine the scenarios for validating the user story during refinement, and developers and testers should implement those tests together as unit tests in the project and the other layers of testing via different tools.
At the end of the day, everyone is responsible for creating a well-functioning application. We should all strive to make that happen, not just dump a user story on the testing status when development is done and get moody when it gets sent back due to bugs.
I would say that only projects there as experiments/proof of concepts and never make it to the production stage could be done without unit testing. Every other project should plan to implement unit testing before the first release or acceptance. And preferably even before any user tests.
It is not a secret that I am a proponent of unit testing. I try to get everyone on board and enable anyone open to it to implement unit tests in their Mendix projects. Even the most significant obstacles become relatively small if you start one step at a time! When you are trying to convince yourself or your team to see the benefits of unit testing, use this blog. I even invite you to contact me and discuss it together with you or your team.
What is next?
Lessons learned from the four blogs already written and my own experiences with unit testing!
Next up, Lessons learned!
Previous unit testing blogs
And while you are at it, look at the meetup recording about unit testing from January 2024.