+ Start a Discussion
paul-lmipaul-lmi 

Custom Settings cause testMethods to fail with null pointer exceptions

When we made the move from using Custom Labels to store app settings to using the new hierarchical Custom Settings to do so, our testMethods started failing due to null pointer references.  From what I've seen, when you go to install a package with Custom Settings definitions via AppExchange, even if you set defaults for it when creating the settings, they are not passed to the target org.  The target org seems to be required to hit the Manage button and accect/create/modify the default settings for the org wide defaults before they can be used.  This is a problem since these won't exist during an AppExchange install, and can't be managed until the install would be done, at which point it's already failed out our testMethods that call code referencing expected default values.

 

What is the proper way to use Custom Settings and still ensure that code coverage is achieved, and that they can be packaged for the AppExchange without defaulting to being null?

Best Answer chosen by Admin (Salesforce Developers) 
Scott.MScott.M

Hi,

 

The custom settings show up as an object in salesforce. So in your tests you can setup test settings by inserting Custom Settings objects. Just make sure that you wrap the custom settings in a runAs block so that you don't hit the mixed dml governor limit. For example if you had a Custom Settings object called MyCustomSettings with one attribute MyValue you could initialize the object in the test with the following code:

 

 

User thisUser = [ Select Id from User where Id = :UserInfo.getUserId() ];
System.runAs( thisUser ){

MyCustomSettings__c mycs = MyCustomSettings__c.getValues('CustomValues');

if(mycs == null) {
mycs = new MyCustomSettings__c(Name= 'CustomValues');
mycs.MyValue__c = 'Test';
insert mycs;
}
}

 

Then when you run your test the custom settings will be set even if the organization you're installing into doesn't have custom settings initialized.

 

Hope that helps :)

 

Cheers,

Scott

 

 

Message Edited by Scott.M on 12-08-2009 01:31 PM

All Answers

Scott.MScott.M

Hi,

 

The custom settings show up as an object in salesforce. So in your tests you can setup test settings by inserting Custom Settings objects. Just make sure that you wrap the custom settings in a runAs block so that you don't hit the mixed dml governor limit. For example if you had a Custom Settings object called MyCustomSettings with one attribute MyValue you could initialize the object in the test with the following code:

 

 

User thisUser = [ Select Id from User where Id = :UserInfo.getUserId() ];
System.runAs( thisUser ){

MyCustomSettings__c mycs = MyCustomSettings__c.getValues('CustomValues');

if(mycs == null) {
mycs = new MyCustomSettings__c(Name= 'CustomValues');
mycs.MyValue__c = 'Test';
insert mycs;
}
}

 

Then when you run your test the custom settings will be set even if the organization you're installing into doesn't have custom settings initialized.

 

Hope that helps :)

 

Cheers,

Scott

 

 

Message Edited by Scott.M on 12-08-2009 01:31 PM
This was selected as the best answer
paul-lmipaul-lmi

excellent!  i converted your example to a hierarchy as follows

 

 

User thisUser = [ Select Id from User where Id = :UserInfo.getUserId() ];
System.runAs( thisUser ){

MyCustom__c settings = MyCustom__c.getInstance();

if(settings == null) {
settings = new MyCustom__c();
settings.field__c = 'test';
insert settings;
}
}

 

one more quick question.  is there an efficient way to execute this code for the entirety of a test class, rather than inside every testMethod?  i'd like to keep the testMethods as compact as I can, if possible.

 

Message Edited by paul-lmi on 12-08-2009 08:38 PM
Scott.MScott.M

So far the only way we've found is to create a public class with a static method for setting up the data, and then calling the function from with in our tests. Something like:

 

public with sharing class TestFixtures { public static void SetupConfig() { User thisUser = [ Select Id from User where Id = :UserInfo.getUserId() ]; System.runAs( thisUser ){ MyCustom__c settings = MyCustom__c.getInstance(); if(settings == null) { settings = new MyCustom__c(); settings.field__c = 'test'; insert settings; } } } }

 

It's not great but I guess it's better than repeating the code in each test. There may be a better way, if you find one let me know! :) 

 

Cheers,

Scott

 

paul-lmipaul-lmi
definitely better.  i'll also not to anyone reading that in order to get this to work for multiple testMethods, we had to issue a DML delete statement at the end of every testMethod, to avoid a duplicate row error being thrown.  not a big deal, but just another stumbling point.
mkdjnsmkdjns

For anyone running across this post and using something beyond API 21, it's no longer good enough to check if the Custom Setting obect is null. Instead, you should check if the Custom Setting object ID is null. So:

 

MyCustom__c settings = MyCustom__c.getInstance();
if(settings == null) {
     //insert new custom setting
}

 Doesn't work since a custom setting object is returned but is empty. Instead you should use:

MyCustom__c settings = MyCustom__c.getInstance();
if(settings.id == null) {
     //insert new custom setting
}

 

cpohlmancpohlman

Outstanding!  This is very helpful for anyone adding a Custom Setting on what you need to add to the test class.  Thank you.