Friday, April 18, 2014

Make Custom Setting use test-friendly in Apex

Custom Setting makes Apex code more configurable; code behaviors can be changed by altering setting values without needing redeployment.  That's especially valuable in a fast-moving business world.  Because of that merit it's a best practice to use it for many scenarios.  However with Custom Setting records treated as data in Salesforce, the downside is in unit test methods they are invisible by default.  SeeAllData = true can certainly be used to make them visible, but that's against best practice and not an option most of the time.  With that issue the introduction of new Custom Setting in code could often lead to headache for test methods, especially when the setting is referenced in trigger.  For instance you added this setting in your Account before trigger to facilitate a special owner routing:

UltimateOwnerDefaults__c UltimateOwnerDefaults = UltimateOwnerDefaults__c.getInstance();

Map ultUsers = new Map([select Id from User where ProfileId = :UltimateOwnerDefaults.Ult_User_Profile_ID__c]);

Congratulations!  You just give the gift of "Attempt to de-reference a null object" to many rule abiding test writers in the org.  All those nicely done test methods (existing or future) will see no custom setting record and bomb out if they try to insert a dummy account record that largely don't really care about owner routing.

Some may advocate the pattern of using a standard environment prep procedure in all test methods.  I'm never a fan of that because that has its own maintenance challenge, and it's a waste of processing time, and code character quota.  My solution is the principle of "if you create it, make it friendly".  It's actually pretty easy to do:

  1. Skip processing related to the setting if you don't have a setting record.  In the above example, if UltimateOwnerDefaults == null, don't continue to the map loading and anything related to it.
  2. If you do still want to still process things in the absence of expected setting records (as a default behavior), provide a default value (even just hard-coded) then.
  3. If neither 1 or 2 a good option in real life, handle the situation (absent setting) in an exception-free way (for instance, catch and log it in some way).  Really, I can't think of a good reason to have an unhandled exception coming out of a trigger for expected situations.
Done that way your new Custom Setting will never be a problem for any test methods that don't really care about it, and your code is more robust as a nice by product.

No comments:

Post a Comment