7

I'm attempting to design a Unit Test Utility that we can use across our 15++ UE Orgs. I'm aiming to accomplish the following goals:

  1. Reads from a declaratively-maintained map of required field/values for multiple objects.
  2. Accepts a field/value map argument on insert to allow different apps to insert app-specific values.
  3. Can generate a variable number of test records, while generating distinct names & External IDs.
  4. Can chain related records together, particularly run-time acquired Record Types, generated Owners and Custom Settings.

I've considered test.loadData, but it fails #2 and #3

I've considered an instance method, but it fails #1: not easily updated when admins add a new Validation Rule

I've considered Custom Settings, but they are not accessible metadata in Unit Tests.

I've considered a JSON file in Static Resources, but according to documentation, they're not listed as accessible metadata (although test.loadData implies they are).

Thanks for any suggestions, warnings and especially solutions!

3
  • 3
    The builder pattern works well where you want concise but flexible data creation. But I would keep these in the language of the domain and so have one or more builder classes per app. The methods can incorporate e.g. JSON data from static resources or custom settings.
    – Keith C
    Oct 23, 2014 at 21:45
  • 2
    A bit of a tangent approach for unit testing - ApexMocks Framework based on Mockito. Oct 24, 2014 at 0:00
  • StaticResource can most definitely be read in a SOQL call within a testmethod. I use that to create testdata by querying the static resource body and then using JsonDeserialize into sobjects
    – cropredy
    Oct 25, 2014 at 0:26

1 Answer 1

1

I worked on a utility of the type you're speaking of for an app I was doing subcontract work on last year. My objectives weren't nearly as universal as what you're trying to achieve but I think I can share some insights into some of the pitfalls you may encounter.

One part of this app created Events from Opportunity and then mirrored the Event to a custom object which were also synced as updates to Opportunity occurred. There were 4 different kinds of accounts that the Opp dealt with, a contact for each type of Acct, and up to 4 additional contacts of a separate record type. While the relationships were simple look-ups, some of these fields were held in Opportunity as formula fields. Some of them were not required fields for either the Acct or the Contact. When a new Event was created, the Opp.ID, Opp.Name, Opp.OwnerName, Event Start Time, End Time, Event Date and a boatload of info from 3 of the related accounts/contacts (addresses, phone numbers, names, etc) were all strung together into a narrative that became the Event Description.

What I'm trying to convey is that what was required to create an Opportunity was a lot more info than just what's required to create the parent account and primary contact for this Org. Additionally, what was a required field to create the related accounts didn't necessarily include all the fields referenced in the formula fields on Opportunity, let alone that were passed into the related Event. Many of those dependencies were hidden, not just in that relationship but in other relationships I dealt with on this project as well.

I was simply hired to write the code for the new triggers/classes to add new functionality; not to fix what was obviously held together with baling wire and chewing gum.

Here's the bottom line of where I'm going with this: had I relied on using a schema describe call to determine what was a "required field" when creating new objects for my test class, I'd have missed a great number of dependencies. I suspect you'll likely encounter the same thing when you look at your "declarative map" as well (even in the best of orgs, something can get missed). Instead, you may want to look at the metadata from your triggers and classes to see what they also need that may not show up in your declarative map when you begin creating your objects.

In my view, #3 and #4 on your list are essential elements of anything your want to build. I think you can tell from my description, I had many related records that needed to be created in a particular order, maintain their record types, owners (or track them so I could change them in a trigger), etc. What I had created wouldn't have been at all useful to me without those features. As my test utility got more sophisticated, I was also able to pass in the number of objects I wanted to create at run time for a unit test.

With reference to item #2, I can see where you might have the need for being able to specify values although it wasn't something I encountered with my test utility. I randomized values in my tests as appropriate (dates and times for example). Other values like street addresses, phone numbers & zip codes were typically sequential with a few things remaining static (like the State).

Generally speaking, I think you're on the right track, I'd just recommend you find a method to check to see that you're not missing hidden dependencies your declarative map may overlook or not point out to you.

1
  • You are correct, but I think the main goal is to cover those items that are not expected. A developer still has to understand the goal of the test methods and populate the object according to the business requirements. No App can do this. However, when it comes to longevity of those test methods, additions in the future of required fields, validation rules, etc that are not part of the defined process at the time the test method is written, is a key item to overcome. If those additions no longer break the test method then we are a step ahead....
    – Eric
    Oct 24, 2014 at 18:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .