Wrapping Custom Settings to Provide Defaults and Ease Unit Testing

Marconi Valve TunerI have a little pattern I use for wrapping custom settings. It has two uses, first to provide sensible defaults if the settings have not been configured and secondly to make writing unit tests easier with predictable values.

If you’ve ever deployed a new bit of code that runs fine in your sandbox because you have all your custom settings configured but then fails in production as they’ve not been set up then this pattern is for you.

Hierarchy Settings

We’ll start with hierarchy settings since this is the simplest wrapper. Take the custom settings below:

Hierarchy Custom Settings

We have 2 properties, a boolean and a string.

Here’s the wrapper class:

public with sharing class MyHierarchySettings
{
	@TestVisible private static MyHierarchySettings__c s_settings;

	// Cache the settings
	private static MyHierarchySettings__c getSettings()
	{
		if (s_settings ==  null)
		{
			s_settings = MyHierarchySettings__c.getInstance();
		}

		return s_settings;
	}

	public static Boolean getMyBooleanValue()
	{
		return getSettings().My_Boolean_Value__c;
	}

	public static String getMyTextValue()
	{
		MyHierarchySettings__c settings = getSettings();

		// Provide a default if it's not initialised
		if (settings.My_Text_Value__c == null)
		{
			return 'henrietta';
		}
		
		return settings.My_Text_Value__c;
	}
}

You’ll see that we have a static variable to cache the settings and a getSettings() method which caches the custom settings. It’s also cunningly visible to unit tests. We then have a getter for each property and in the case of the Text setting, returns a default. We’ve no need to return a default for the boolean since it will happily return false and that’s a good enough default for me in this scenario. It’s worth noting that the getInstance() method always returns an object for a Hierarchy setting even if it’s not been defined yet. You’ll see later this is not true of List settings.

So to demonstrate we have a unit test:

@isTest
private class MyHierarchySettingsTest 
{	
	@isTest static void test_getMyBooleanValueDefault() 
	{
		System.assertEquals(false, MyHierarchySettings.getMyBooleanValue());
	}
	
	@isTest static void test_getMyTextValueDefault() 
	{
		System.assertEquals('henrietta', MyHierarchySettings.getMyTextValue());
	}
	
	@isTest static void test_setMyBooleanValue() 
	{
		MyHierarchySettings.s_settings = new MyHierarchySettings__c(My_Boolean_Value__c = true);
		System.assertEquals(true, MyHierarchySettings.getMyBooleanValue());
	}
	
	@isTest static void test_setMyTextValue() 
	{
		MyHierarchySettings.s_settings = new MyHierarchySettings__c(My_Text_Value__c = 'scarlet');
		System.assertEquals('scarlet', MyHierarchySettings.getMyTextValue());
	}
}

You’ll see in the unit tests how we can make use of the static variable to initialise the custom setting with our own values. This allows us to write a unit test with predictable data and is much tidier than inserting custom settings data.

List Custom Settings

List custom settings are a little more complicated. Let’s look at the sample settings below:

List Custom Settings

Again I have just a Boolean and a String property. So let’s look at the wrapper class for this:

public with sharing class MyListSettings
{
	@TestVisible private static Map<String, MyListSettings__c> s_settingsMap;

	// Cache the settings
	private static MyListSettings__c getSettings(String key)
	{
		if (s_settingsMap == null)
		{
			s_settingsMap = new Map<String, MyListSettings__c>();
		}

		MyListSettings__c settings = s_settingsMap.get(key);

		if (settings ==  null)
		{
			settings = MyListSettings__c.getInstance(key);
			s_settingsMap.put(key, settings);
		}

		return settings;
	}

	public static Boolean getMyBooleanValue(String key)
	{
		MyListSettings__c settings = getSettings(key);

		// Provide a default if it's not initialised
		if (settings == null)
		{
			return false;
		}

		return settings.My_Boolean_Value__c;
	}

	public static String getMyTextValue(String key)
	{
		MyListSettings__c settings = getSettings(key);

		// Provide a default if it's not initialised
		if (settings == null || settings.My_Text_Value__c == null)
		{
			return 'henrietta';
		}
		
		return settings.My_Text_Value__c;
	}
}

You’ll notice this time the static variable is a map keyed on the setting name. The getSettings() method still caches the data but stores it in the map. The getters for the properties take an argument of the key to the List. You’ll also notice that we have to explicitly supply a default for the Boolean. This is because the getInstance(key) method will return null if there is no List setting with the specified key.

On to the unit test to demonstrate:

@isTest
private class MyListSettingsTest 
{	
	@isTest static void test_getMyBooleanValueDefault() 
	{
		System.assertEquals(false, MyListSettings.getMyBooleanValue('geoff'));
	}
	
	@isTest static void test_getMyTextValueDefault() 
	{
		System.assertEquals('henrietta', MyListSettings.getMyTextValue('geoff'));
	}
	
	@isTest static void test_setMyBooleanValue() 
	{
		MyListSettings.s_settingsMap = new Map<String, MyListSettings__c> {'geoff' => new MyListSettings__c(My_Boolean_Value__c = true)};
		System.assertEquals(true, MyListSettings.getMyBooleanValue('geoff'));
	}
	
	@isTest static void test_setMyTextValue() 
	{
		MyListSettings.s_settingsMap = new Map<String, MyListSettings__c> {'geoff' => new MyListSettings__c(My_Text_Value__c = 'scarlet')};
		System.assertEquals('scarlet', MyListSettings.getMyTextValue('geoff'));
	}
}

In the same way as the hierarchy test we can use the static map variable to initialise it with predictable data.

Advertisements

Author, brainstormer, coder, dad, explorer, four chord trickster, gig goer, home worker, inquisitor, joker, knowledge seeker, likes: marmite, note scribbler, opinionator, poet, quite likes converse, roller skater, six music listener, tea drinker, urban dweller, vinyl spinner, word wrangler, x-factor hater, Yorkshireman (honorary), zombie slayer (lie).

Tagged with: , , ,
Posted in apex, code, force.com, salesforce
About Me
Product Services Developer at:
FinancialForce.com
All views expressed here are my own. More about me and contact details here.

Enter your email address to follow this blog and receive notifications of new posts by email.

Copyright (there isn’t any, feel free to reuse!)

CC0
To the extent possible under law, Tony Scott has waived all copyright and related or neighboring rights to MeltedWires.com Examples and Code Samples. This work is published from: United Kingdom.

%d bloggers like this: