Wrapping Custom Metadata to Support Unit Testing

Wrapping PaperMy last post looked at using a wrapper class to make Custom Settings more convenient to access from apex code and to ease defaulting and unit testing.

If, like me, you’ve taken the leap to using Custom Metadata instead of List based Custom Settings you may have found yourself with a headache when writing your unit tests. Sure, you don’t need SeeAllData=true to see your metadata from your unit test but you may want to make sure the settings are in order for your given test.

Unlike Custom Settings, you can’t use DML to inject data for your unit tests. Attempting to instantiate a custom metadata object results in the rather friendly:

[OPERATION FAILED]: classes/MyClass.cls: Field is not writeable

To get round this I’ve adapted the pattern from my last post to support Custom Metadata.

Lets use the example Custom Metadata defined here:

Custom Metadata Definition

Nothing too exciting, a checkbox and a text field but it’ll do to demonstrate.

So we’ll start with the wrapper:

/**
 * Wrapper for My_Custom_Metadata_Type__mdt	to ease access and testing
 */
public with sharing class MyCustomMetadataTypes
{
	// Cached Map of metadata
	@TestVisible private static Map s_myCustomMetadataTypeMap = new Map();

	// Public getter
	public static Metadata getMyCustomMetadataType(String developerName)
	{
		// Check the cache
		Metadata myMetadata = s_myCustomMetadataTypeMap.get(developerName);

		// Not found, so query the metadata
		if (myMetadata == null)
		{
			My_Custom_Metadata_Type__mdt myCustomMetadataType;

			try
			{
				myCustomMetadataType = [Select DeveloperName, My_Checkbox__c, My_Text__c
				    					From My_Custom_Metadata_Type__mdt
				     					Where DeveloperName = :developerName];
			}
			catch(System.QueryException sqe)
			{
				throw new MyCustomMetadataTypeException('Could not find My Custom Metadata Type with DeveloperName: ' + developerName);
			}

			// Wrap the metadata using the inner class
			myMetadata = new Metadata(myCustomMetadataType);

			// Add it to the cache
     		s_myCustomMetadataTypeMap.put(myCustomMetadataType.DeveloperName, myMetadata);
		}

		// Return the wrapped metadata
		return myMetadata;
	}

	// Inner class to wrap the metadata
	public class Metadata
	{
		// Properties represent the Fields in the Metadata
		public Boolean My_Checkbox {get; set;}
		public String My_Text {get; set;}

		// Constructor used when creating test values
		public Metadata()
		{
		}

		// Construct from a concrete metadata instance
		private Metadata(My_Custom_Metadata_Type__mdt metadata)
		{
			this.My_Checkbox = metadata.My_Checkbox__c;
			this.My_Text = metadata.My_Text__c;
		}
	}

	public class MyCustomMetadataTypeException extends Exception {}
}

You’ll notice we have an inner class that reflects the fields that we’ve defined in the Custom Metadata.

It has two constructors, a public one we can use for mocking test data and a private one for marshalling an instance of the metadata in the real world. We’ll always use this inner class to get metadata valus and never reference the metadata directly.

A static Map of Developer Names to instances of the inner class provide the way to set values for Unit Tests. It’s set to TestVisible for this purpose. It’s also used to cache values whenever the getter is called. While it’s not strictly necessary to cache the data as Salesforce takes care of that, it does help support the pattern.

The public getter simply takes an argument of a developer name and returns an instance of the inner class populated with the metadata. So in you’re production code you can do things like:

String myString = MyCustomMetadataTypes.getMyCustomMetadataType('indierock').My_Text;

If the developer name doesn’t exist it will throw an exception which you can catch.

I’ll now show you some unit test examples and we’re done:

@isTest
private class MyCustomMetadataTypesTest
{
	@isTest static void thisWontWork()
	{
		// You can't do this as the metadata fields are not writeable!
		// Result: [OPERATION FAILED]: classes/MyCustomMetadataTypesTest.cls: Field is not writeable: My_Custom_Metadata_Type__mdt.DeveloperName

		/*
		My_Custom_Metadata_Type__mdt metadata = new My_Custom_Metadata_Type__mdt(
			DeveloperName = 'test',
			My_Checkbox__c = true,
			My_Text__c = 'some text'
		);

		insert metadata;
		*/
	}

	@isTest static void thisWorks()
	{
		// Create our test metadata
		{
			MyCustomMetadataTypes.Metadata metadata = new MyCustomMetadataTypes.Metadata();
			metadata.My_Checkbox = true;
			metadata.My_Text = 'some text';

			MyCustomMetadataTypes.s_myCustomMetadataTypeMap.put('test', metadata);
		}
		{
			MyCustomMetadataTypes.Metadata metadata = new MyCustomMetadataTypes.Metadata();
			metadata.My_Checkbox = false;
			metadata.My_Text = 'some more text';

			MyCustomMetadataTypes.s_myCustomMetadataTypeMap.put('test2', metadata);
		}

		// Use the wrapper to access the metadata
		MyCustomMetadataTypes.Metadata metadata = MyCustomMetadataTypes.getMyCustomMetadataType('test');

		// Assert the results
		System.assertEquals(true, metadata.My_Checkbox);
		System.assertEquals('some text', metadata.My_Text);

		// Use the wrapper to access the metadata
		MyCustomMetadataTypes.Metadata metadata2 = MyCustomMetadataTypes.getMyCustomMetadataType('test2');

		// Assert the results
		System.assertEquals(false, metadata2.My_Checkbox);
		System.assertEquals('some more text', metadata2.My_Text);

		// This demonstrates the exception thrown when the metadata developer name can't be found
		try
		{
			MyCustomMetadataTypes.Metadata metadata3 = MyCustomMetadataTypes.getMyCustomMetadataType('test3');
			System.assert(false, 'Expected an Exception.');
		}
		catch(Exception e)
		{
			System.assert(e instanceof MyCustomMetadataTypes.MyCustomMetadataTypeException);
		}
	}

	@isTest static void thisAlsoWorks()
	{
		// Using some Custom Metadata already defined in my org. Look, no SeeAllData=true!

		// Use the wrapper to access the metadata
		MyCustomMetadataTypes.Metadata metadata = MyCustomMetadataTypes.getMyCustomMetadataType('indierock');

		// Assert the results
		System.assertEquals(true, metadata.My_Checkbox);
		System.assertEquals('The Cribs', metadata.My_Text);
	}
}

 

 

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
One comment on “Wrapping Custom Metadata to Support Unit Testing
  1. […] via Wrapping Custom Metadata to Support Unit Testing — melted:wires […]

Comments are closed.

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: