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.