How mocking simplifies unit testing in Salesforce projects

How mocking simplifies unit testing in Salesforce projects
Photo by JJ Ying / Unsplash

In order to build anything useful, the code we write must be connected. Connected to other code - classes, selectors, and services. Connected to databases. Connected to apis. Even connected to components that haven't been built yet.

These connections, called dependencies, add challenges and complexity that draw a developer’s focus away a unit test’s primary task - proving that one small chunk of code works as expected and is bug-free.

This post describes 5 problems caused by dependencies that are very likely slowing down your unit testing. It then goes on to introduce a technique called mocking which solves many of these issues, allowing you as a developer to get back to what’s important - perfecting your one unit of code.

What problems do dependencies present for unit testing?

Dependencies can be buggy
Why is your test failing? Well, maybe there's a bug in the code you've just written that needs fixing… but then again, maybe the problem is in one of its dependencies... or, one of their dependencies.

As you go down a rabbit hole of connected components looking for the root cause, debugging time increases accordingly.

Dependencies can have high setup costs
Every Salesforce developer knows the eternity it can take to set up test data in the database.

It’s rare you can insert just the records that your component will process during a test. Those records will likely have a whole network of required parent objects which you’ll also need to create. Even more errors will then be thrown by database constraints at the field level, where values are restricted by value sets or validation rules.

You end up spending more and more time concerned with data issues that are totally irrelevant to your test. So you’ve got less time to actually focus on the logic in your component.

Dependencies can return unstable results
Unit tests always have to run the same way every time to return consistent results, but some dependencies, such as APIs, are inherently unstable. Whether it’s the latest exchange rate data or a Twitter search, the results will change over time, even moment to moment.

Dependencies can behave erratically
Intermittent behaviour such as an API going down temporarily might very occasionally fail your test, so this needs to be controlled to ensure your test results are consistent.

Additionally, because systems can and do go down in production, you need to know that your code can handle these scenarios and respond appropriately.

But scenarios like these are hard to test with live systems. They’re tricky (and expensive) to set up, and even harder to automate.

Lastly, some dependencies don't even exist yet
You could be building a component that relies on code, or an API, or a system, that is still being built (by another teammate, or another team, or another company).

So, what do you do until the finished version is available? Wait?

The solution to these issues: Mocking.

Mocking is a unit testing technique that swaps out real dependencies with fake versions called mock objects.

Stubbing
In just a few lines of code, mock objects can be programmed (stubbed) to mimic the behaviour of real dependencies.

So, you might program a mock selector to return the data that a real database query would have returned:

”When this selector method is called with a set of IDs, return 3 Invoice records.

Verify
Mock objects also record every interaction with your component. After the test executes, you can then query the resulting log to answer questions about the internal execution of your code.

This allows you to verify behaviour in ways that you can’t always do just by checking the state.

”When the API was down, did my service retry the HTTP request 3 times?”

So, how does mocking actually help?

Mocking cuts the connection between the code being tested and its dependencies. It is no longer connected to the real API, database, or 3rd party system, but to one or more mock objects.

Control
You can rely on mock objects to behave exactly how you program them to behave… every time. You determine the shape, content, timing and sequence of every response.

It means that previously tricky to set up scenarios like an API going down, or throwing an error, are trivially easy to set up.

You’re in complete control of all input to the component under test.

Speed
It’s much faster too.

Firstly in terms of setup costs. You’re no longer battling infrastructure, database constraints, or wasting time inserting irrelevant test data. Instead, you can mimic any desired response in just a few lines of code.

Secondly, those in-memory, preprogrammed mock responses return virtually instantaneously, so previously slow processes that involved database queries or API callouts now run blazingly fast.

Isolation
Cutting the connection to real dependencies also isolates your component.

This protects it from buggy code elsewhere in the system. So if your test does fail, you’ll find the debugging time is much reduced.

There’s now only one place to look.

Efficiency
Decoupling from real dependencies also gives you a mechanism for building interconnected systems in parallel.

As soon as teams have agreed the contracts for how each system will interact, they can use mock dependencies that return data of the correct shape until the real ones are finished.

Conclusion

As well as providing a way to avoid heavyweight test setup, stabilise unit tests, and quickly set up a wide range of test scenarios, mocking simplifies the unit testing experience for the developer.

It means you don’t need to think beyond the edges of the component you’re working on. Your focus is brought back to just 3 elements - perfecting the code in a single method, controlling its input, and verifying its output.

I’ve found that this results in a less stressful and more efficient testing process, leading to more tests actually getting written and less bugs reaching production.