Saturday 3 July 2021

Low Code Still Needs Unit Tests


First off let me make clear this is not dumping on low code or some King Canute like attempt to push back the advance of clicks not code in favour of having to write every tiny automation in Apex. When I started working with Salesforce back in 2008, I didn't look at workflow rules and think "Oh nose, I wanted to write a bunch of code to update the contents of a rich text area field", I thought 'That's cool, someone else can configure the simple automation and I get to work on the more challenging scenarios". 

On the developer side we've spent years convincing people that unit tests are a good thing, and we even have test driven development that turns the requirements into tests as a first step, after which we write the code that allows the tests to pass. Some people still moan about having to write tests to deploy to production, but most of us recognise this is a good thing and we'll be grateful we had to do it at some point in the future. 

So when I see messaging (from Salesforce and many others) that not being able to write unit tests is a benefit of whatever Low Code platform is being talked about, it strikes me as an attempt to brand a negative as a positive without explaining the downsides. You might be able to get things into production quicker, but realistically you should be swapping unit test effort for extended QA and UAT effort, not just trousering the difference. If you don't, it's quite likely you are getting bugs into production quicker!

Unit Testing is Important

Unit testing is basically writing code that verifies that other code works as expected. Now that low code includes more and more business logic, and the cyclomatic complexity is increasing, unit testing is something which is needed more than ever. As an aside, I felt the same when Aura components were first introduced and moved a bunch of business logic to the front end, so I did a talk at Dreamforce on unit testing those.  

Unit testing brings the following benefits, which apply equally to all types of code:

  • The nearer you find a bug to the developer, the less it costs to rectify.
  • With good test coverage and appropriate assertions, you can make changes with confidence that if you break behaviour that someone else is relying on, one or more tests will fail (this is the main benefit for me).
  • If someone else makes a change that breaks your code (adding a validation rule is the classic situation here), the next time the unit tests run this will be picked up. 
  • It keeps your users happier, as more bugs are found during the development cycle rather than after the system has been released to them.

Testing No and Low Code

No Code tends to be discrete units that can be used to apply automation, like a workflow rule that sent an email and updated some fields if a record transitioned to a specific state, or a validation rule that checks dependencies between fields. There's no point in unit testing that with many different combinations of record fields, for the same reason that in Apex we don't write unit tests to check that the compiler and run time is working as expected. The workflow engine is tested by Salesforce and we can trust it. Manually running a few records through is fine for testing a scenario like this, as the logic that causes the workflow or validation rule to fire is typically very small and rarely changes.

In Low Code, a number of these kind of items are assembled into some more complex automation in a flow, including conditional logic and loops, which is much closer to traditional code. The medium of expression might be different, but we are now in a world of complexity and reuse, especially if it's a subflow that can be dropped into any other flow. In this world it makes sense to run the flow with a variety of inputs, with the underlying database in a variety of states, and as users with a variety of profiles, and many different combinations if these. Doing all this manually every time there is a change isn't going to be practical. Doing it via test automation tools is possible, but tends to be testing an entire transaction rather than the smallest unit, and it can't happen in production as the changes aren't rolled back at the end of the test.

Right now the only way to do this is in a genuine test environment is to write Apex tests. This somewhat defeats a key benefit of flow for smaller organisations - they would still need a developer on staff to write the unit tests for the flows that the admins have created. (Slightly off topic, I also think it would be hard to find a developer who wanted that job for any length of time, so you'd have to be prepared for some significant churn). Apex can't hook directly into the flow engine in all cases though, so for record triggered there's a compromise that the transaction must actually complete, after which verification that the flow aspect had the desired effect can take place. This is more like an integration test, as there are other aspects of automation taking place that could impact the work done by the flow, but it's definitely better than nothing. Once you have Apex tests, you can take advantage of many existing solutions for scheduled execution, making them part of a deployment pipeline etc.

What Might the Future Hold

Ideally what we need is a Low Code test builder that can execute flows/subflows in isolation and generates actual unit tests, with assertions, that run in an isolated environment where the transactions are rolled back open completion. Generating Apex seems like a reasonable approach to me, as does targeting another engine. Given all the effort that has gone into flow over the last few years, it feels like this should be doable. 

Many years ago when Salesforce was seen as shadow IT, my approach to bring the technology departments into the fold was to tell them how their governance and best practice was needed to ensure that things were done properly and were able to scale. Bringing best practice from the Pro Code world feels like a good way to get everyone on board with the Low Code approach.

1 comment:

  1. Agree with you that low code also must be covered with unit tests. But it surprises me how Salesforce does everything so that we do not write unit tests for low code. I'm speaking about the impossibility of deploying flows as active in sandboxes. How Salesforce expect me to build CI and write unit tests for Flows if I cannot perform validation deployment in Sandbox with test active? My unit tests would simply fail.

    Have you managed to solve that issue?