I recently wrote an API to implement Account/Contact Duplicate/Matching rules for a connected app. It all works fine.
It does the same thing that Salesforce does for these rules. It pretty much follows this example code:
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_Datacloud_DuplicateResult.htm
I'm now trying to write a test class for this but I'm hitting a roadblock.
I need a way to do one of two things:
-
Create Duplicate and Matching rules through Apex code within the test. I would create them, run the test, then delete them.
-
Turn Duplicate and Matching rules on and off through Apex code within the test. Same general idea but I could create them beforehand in our testing org.
As far as I can tell there is no way to do this. Am I missing something here?
If this isn't possible then how do I get test coverage on my class. It will only get full code coverage if the call to Database.Insert actually fails with DuplicateErrors.
Edit: I should add that having rules always on and bypassing them with DMLHeader for testing is not an option.
Edit2: This is the code I am trying to test:
Database.SaveResult saveResult = Database.insert(contact, false);
if (!saveResult.isSuccess()) {
for (Database.Error error : saveResult.getErrors()) {
// If there are duplicates, an error occurs
// Process only duplicates and not other errors
// (e.g., validation errors)
if (error instanceof Database.DuplicateError) {
// Handle the duplicate error by first casting it as a
// DuplicateError class
// This lets you use methods of that class
// (e.g., getDuplicateResult())
Database.DuplicateError duplicateError =
(Database.DuplicateError)error;
Datacloud.DuplicateResult duplicateResult =
duplicateError.getDuplicateResult();
// Display duplicate error message as defined in the duplicate rule
ApexPages.Message errorMessage = new ApexPages.Message(
ApexPages.Severity.ERROR, 'Duplicate Error: ' +
duplicateResult.getErrorMessage());
ApexPages.addMessage(errorMessage);
// Get duplicate records
this.duplicateRecords = new List<sObject>();
// Return only match results of matching rules that
// find duplicate records
Datacloud.MatchResult[] matchResults =
duplicateResult.getMatchResults();
// Just grab first match result (which contains the
// duplicate record found and other match info)
Datacloud.MatchResult matchResult = matchResults[0];
Datacloud.MatchRecord[] matchRecords = matchResult.getMatchRecords();
// Add matched record to the duplicate records variable
for (Datacloud.MatchRecord matchRecord : matchRecords) {
System.debug('MatchRecord: ' + matchRecord.getRecord());
this.duplicateRecords.add(matchRecord.getRecord());
}
this.hasDuplicateResult = !this.duplicateRecords.isEmpty();
}
}
Best Answer
Duplicate Rules and Matching Rules cannot be created or modified with Apex (not in a normal context, and not in a test context).
In situations where you need to test something that you don't have direct control over, or if it's somewhere between hard and impossible to set up a situation where thing X happens, it's often helpful to add a layer of abstraction.
For code that relies on something like Custom Metadata Types, you'd introduce a proxy class that you would use instead of calling
My_Custom_MDType__mdt.getInstance()
. To make it useful in tests, you would want that proxy class to provide a way to set (and use) specific test data.Something like
You can create instances of CustomMetadataTypes in memory (we just can't perform DML on them), so it's simple to create an appropriate instance in a test and call
setMDTCache()
.In your particular situation, you should consider adding a layer of abstraction to your DML calls. Your concern here isn't really that your duplicate/matching rules work (that's, at the very lowest level, a concern for an integration test.) It's also a standard feature provided by Salesforce, so you should trust that they are testing it (and there are no issues).
Your concern here is what happens in your code when you encounter a
Database.SaveResult
with an error of typeDatabase.DuplicateError
. So you should create a proxy class that can return such data on demand.Unfortunately,
Database.SaveResult
,Database.Error
,Database.DuplicateError
and the like are all non-constructable and non-extendable. So in order to test this, you'll need to: