24

I have three questions that I'd like to be clarified.

  1. Is their anyway we could create Custom Metadata in Test Classes? I tried to create on my Test Class but I got an error saying the fields of the metadata could not be writable.

  2. If not, then does that mean we should declare real values in our Custom Metadata and use that instead in our test classes?

  3. If (2) is correct, then does this mean we have to create the Custom Metadata in the environment we'll be deploying our code before doing the actual deployment?

16

There is possible hack to have different values in tests than it is in organization.

This is a good way to have your tests always stable and 100% covered even if custom metadata is changed or removed on your organization.

For this hack you just need to put query for custom metadata into separate property which should be settable for unit tests.

@testVisible static List<Custom_Metadata__mdt> customMetadata { 
    get {
        if ( customMetadata == null )
            customMetadata = [ SELECT All_Needed_Custom_Fields__c FROM Custom_Metadata__mdt ]; 
        return customMetadata;
    } set; }

Then you can use JSON.deserialize method to setup these metadata records. This method allows you to overcome 'field is not writable' error.

Example of code you could use is the following:

CustomClass.customMetadata  = (List<Custom_Metadata__mdt>) JSON.deserialize( '{"All_Needed_Custom_Fields__c ":"dummyValue"}', List<Custom_Metadata__mdt>.class )

Expanded example using JSON.deserialize by @andrew-fawcett to create the JSON payload per field.

enter image description here

| improve this answer | |
  • I think this is an excellent solution and not a hack; Use of Json.deserialize to mock sobjects otherwise unmockable works well; I also put access to custom metadata lists into a selector layer where I can then use mock selectors and the fflib Force.Com Enterprise Architecture pattern within a testmethod – cropredy Dec 16 '16 at 19:49
  • Actually this solution was found by one of my juniors. If you agree that this is excellent solution then why don't you upvote my answer? If you use mock selectors you might not have the 100% coverage if I understand your solution. If you are talking about some test metadata then that solution will fail if some configuration admin deletes accidentally those test metadata records while my solution is agnostic about real metadata in the system. – Patlatus Dec 18 '16 at 9:12
  • I don't know why I forgot to upvote. It was certainly my intention. The mock selector makes the regression suite immune from sysad changes to the actual metadata and allows for easy creation of various unit test situations. The code coverage issue I find to be less important for the selector layer of custom metadata types. – cropredy Dec 18 '16 at 15:49
  • I need to update a field from a list of records. I have been trying to do this and missing the update part. "JSON.deserialize" seems to be just giving a value out and not updating it. Please help. – Krishna Jul 11 '17 at 0:42
  • In the new Summer 17 release there is an option to update metadata records using Metadata.Operations class, it is just performing a deploy to organization, you could read more about it here: salesforce.stackexchange.com/questions/179191/… – Patlatus Jul 15 '17 at 11:14
12

1 - Is their anyway we could create Custom Metadata in Test Classes? I tried to create on my Test Class but I got an error saying the fields of the metadata could not be writable.

No, you can't create custom metadata in Apex Code. This is noted as a limitation of the feature in Custom Metadata Types:

Custom metadata rows resemble custom object rows in structure. You create, edit, and delete custom metadata rows in the Metadata API or in Setup. Because the records are metadata, you can migrate them using packages or Metadata API tools. Custom metadata records are read-only in Apex and in the Enterprise and Partner APIs. (Emphasis mine)


2 - If not, then does that mean we should declare real values in our Custom Metadata and use that instead in our test classes?

Yes, that's the only way to test code that uses these values at this time (short of writing shenanigans in your live code (which is not recommended).


3 - If (2) is correct, then does this mean we have to create the Custom Metadata in the environment we'll be deploying our code before doing the actual deployment?

You should be able to migrate both at the same time if you're using a package or change set. The system deploys objects in a specific (but not documented) pattern. Fields, profiles, public groups, custom metadata, custom settings, and other metadata will always be deployed before Visualforce pages, Apex Code, triggers, components, etc. This is noted in the Deployment Dependencies documentation:

Dependencies require that components are deployed in a specific order, and within a single deploy operation, this order is handled automatically by the Metadata API. However, if you deploy a subset of components, or split your deployment into multiple batches, you must take into account the ordering dependencies yourself. (Emphasis mine)

| improve this answer | |
  • Thank you very much for the answers. They really clarified some things that were bothering me. And thank you for also pointing out that the deployment process has a specific order. I'll take note on this one. Again thanks! – hereLiesThisTroper Apr 19 '16 at 3:42
10

Salesforce Developers put out the blog post Testing Custom Metadata Types that includes the section Testing Global Custom Metadata.

...add an extra field to your custom metadata type to indicate what sort of tests the type is active for. For example, you could add a Text field called TestCase__c to your type. You can leave this field blank for production custom metadata and set it for test custom metadata.

enter image description here

Class with static setting to toggle records to retrieve.

public class TestContext {
    public static String testCase {public get; public set;}
}

Queries against the custom metadata type use the static setting to retrieve testing configuration.

OurCustomMetadata__mdt[] rows =
    [SELECT QualifiedApiName, Field1__c
     FROM OurCustomMetadata__mdt
     WHERE TestCase__c = :TestContext.testCase];

Test indicates which configuration to use.

public static testmethod void testWelcomePageWorksWithTestCase1() {
    TestContext.testCase = 'TestCase1';
    /* test the Welcome page */
}

Personally I find the proposed solution a bit ugly and have voted for Custom MetaData Types need APEX methods to access/update.

See also:

| improve this answer | |
6

I've written a lot of code with custom metadata and I haven't been particularly happy with any solutions I've seen online from Salesforce directly or otherwise.

No, there is no way to create custom metadata from test code, nor should there be. It's metadata, and allowing custom metadata to be edited in code erodes the critical distinction between metadata and data. If code can edit metadata, it introduces a level of unpredictability to setup that could make administrators' lives a nightmare. It's also bad practice to test configuration in test code without a very good reason, and given that custom metadata's purpose is almost always to allow the user to change certain configuration parameters, it feel to me a sin as bad as (seeAllData=true).

The prevailing opinion I've seen is to create "test" metadata, but this doesn't make a lot of sense in certain cases. If you're creating Custom Metadata where you use a single record to define system-wide-defaults related to your code, you'd want to test how the code responds to different configuration values. It would be ridiculous to create one "real" record to configure the system, then multiple "test" records to test certain configurations and assert they interact in the code correctly. Creating "test" metadata seems like a reasonable solution in some cases, but in my experience, it's wrong more often than it's right.

Instead, I construct my code in a way so that the Custom Metadata is never referenced exactly except by a class whose purpose is to process and read it. For example:

@testVisible
private static OrderConfigurationSetting setting {
    get{
        if (setting == null){
            Order_Configuration_Settings__mdt metadata = [
                SELECT
                    Batch_Run_Frequency__c,
                    Duplicate_Day_Range__c,
                    Qty_Minimum__c
                FROM Order_Configuration_Settings__mdt
                WHERE DeveloperName = :DEFAULT_SETTING_NAME
            ];
            setting = new OrderConfigurationSetting(
                metadata.Batch_Run_Frequency__c,
                metadata.Duplicate_Day_Range__c,
                metadata.Qty_Minimum__c
            );
        }
        return setting;
    } set;
}

@testVisible
private class OrderConfigurationSetting {
    public String batchRunFrequency;
    public Integer duplciateDayRange;
    public Integer qtyMinimum;

    public OrderConfigurationSetting(
        String batchRunFrequency,
        Decimal duplciateDayRange,
        Decimal qtyMinimum
    ){
        this.batchRunFrequency = batchRunFrequency;
        this.duplciateDayRange = Integer.valueOf(duplicateDayRange);
        this.qtyMinimum = Integer.valueOf(qtyMinimum);
    }
}

This is accompanied by a number of public getters for these attributes to be used in assorted functional code. When writing test code, I'll define the static setting variable to be whatever configuration I need to test. Then, I don't have to ever worry about the values of the Custom Metadata records at all during testing. I also get the added benefit of processing the values in the Custom Metadata before any part of the code uses them (for example, a Number(6,0) will be called as a Decimal but it really should be viewed as an Integer). This may seem like a bit over-engineered, but it's really clean and allows for very painless thorough testing.

| improve this answer | |
  • Fantastic approach, thank you for sharing! – Brian Miller Jul 23 '19 at 9:00
  • I'm unclear on a couple of things: 1) Why is the OrderConfigurationSetting class private? 2) How is "setting" used in production vs test classes? – vr8ce Jul 17 at 22:21
6

This is an old question, but one or two releases ago a new answer became available, so I thought I'd post it here.

You now can instantiate custom metadata records, and set their values, the same way you would with records of any other type. You can't insert them, and they won't work with declarative platform features like formulas (just with Apex), but if you separate your query code from the method that uses the results, you can use this technique to create, in-memory, values to pass that method.

| improve this answer | |
  • 1
    ApexMocks and fflib Selector layer being one example of how to do this. – cropredy Jan 6 '19 at 3:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.