Better Apex tests with domain-specific test data factories

Thiago Rodrigues de Paula
hutte.io
3 min readOct 11, 2019

--

If you don’t follow good software development practices on your test suite the same way you do it when writing your application, your tests can rapidly become unmaintainable, and you can start getting false positives as your software grows.

Test data factories are a great way to prevent these problems. It achieves it by centralizing record creation and guaranteeing that when some business rule changes, like adding a required field to that object, the tests that rely on this object still pass.

While generating test data in a centralized place is an excellent practice, many test data factories that I’ve seen have caveats that are likely to cause headaches in the future.

In this article, I’m going to analyze the downsides of popular implementations and propose a different approach. One that focuses on encapsulating business rules into these factories.

Approach 1: Using the Salesforce-Test-Factory library with explicit attribute declarations

The Salesforce-Test-Factory (https://github.com/dhoechst/Salesforce-Test-Factory) is a frequently used library to generate test data. It does a great job, and that’s why it was the one chosen for comparison reasons.

Now, onto the example.

Given that your test case needs the following records: an active employee; an old employee; and an employee that hasn’t started working yet; You would end up with the following code:

Even though this example works very well, it hides a critical design problem. We had to explicitly set the fields that indicate whether the employees have an active contract, are no longer with the company, or haven’t yet started.

The more test cases involving these rules, the more their implementation repeats itself. That means that whenever the definition of active employee changes, different and unrelated tests might start failing. Even worse would be if they wrongfully continue passing.

What that means is that with every change on the business rules, you would have to remember where the tests involving them were, and fix these tests one by one.

Approach 2: Using the Salesforce-Test-Factory library with a set of overrides

Thankfully, this library supports the concept o a set of overrides for different scenarios. That means that you can create a reusable set of attributes and use them when creating your test data. By following this approach, our tests would look like this:

While solving the flaws mentioned above, this approach introduces a new one. It doesn’t compose well.

Let’s say you have two business rules for the same object, active and part of the C-Level. You’d have two possibilities:

  1. Create different sets of overrides for each business rule. ActiveCLevelEmployee and ActiveEmployee, for example.
  2. Call createSObject using the result of another createSObject as the first parameter.

The obvious problem with the first approach is that you can end up with too many, hard to reuse, combinations. The not so obvious one is that if you change the definition of an active employee, for example, you would have to change the code in many places.

The second approach, while not having design problems, adds too much noise to the test code, making it hard for readers to understand the context the test requires.

Our suggestion: Test Factories that speak the language of the business

At flair.hr, we're taking a different approach. One that we believe solves all the issues mentioned above.

Here is an implementation of the Factory to generate the same employee test data we did before:

And here is how this factory class is used:

The important points of this implementation are:

  • Business rules are encapsulated into the methods of the factory;
  • Required fields of the object are implicitly included in the start() method;
  • All the methods compose well with each other;

Besides that, the tests are now much more readable and understandable because it uses the language of the business and has very little unnecessary noise.

That’s it!

Please share your thoughts when it comes to creating test data, and let us know which approach your team uses. We love discussing software design and learning new things.

--

--