1

We have a custom Prescription object, associated to a Contact/Patient, which can be renewed (extended, essentially) or revised (some significant change) periodically. A renewal is straightforward; a revision requires review.

Prescriptions come in through our portal, and if they're really renewals, they automatically process. As they land in Salesforce, I look at each significant field to determine whether it's different from what we have on the Patient.

private Boolean Revision(Contact c, Prescription__c rx){} evaluates each incoming Prescription and returns true if it's a Revised Prescription (if any significant fields has changed), or false if they haven't. Each field being evaluated goes through a section like this:

if(Prescription__c.field1__c <> Contact.field1__c){
  result = true;
  break;
}

If any one of these fields has changed, there's no reason to check any of the others - stop there, change the recordtype to 'Revision'.

I have unit tests for each of the fields being evaluated:

Prescription__c.field1__c = 'new value';
Contact.field1__c = 'old value';
System.assertEquals(true, RevisionRx(contact, prescription);

And the tests all pass. When I look at the test coverage, however, many of these fields' evaluations remain flagged red, and the test coverage is abysmal. If I run just the one field's test, then it goes blue. ~~~~~~

I thought about creating Utility methods to do the IsChanged() comparison, but it really wouldn't cut down on the code size because I still want to break; if any return true.

I really don't want to break this into individual, field-level methods which might test more easily, and I'd still need to group them all together in a big If/Else If to manage the breaks.

I can't slide by with 'Run All Tests', either -- I need coverage for each Change Set I deploy.

Thoughts?

1 Answer 1

2

If your field names are the same on both objects, there are a couple of different ways you could go about this.

Approach #1

The first method is to define a Set<String> that contains all of the field API labels that you want to test. You can then loop over that set, and make the comparisons one field at a time.

Set<String> fieldsSet = new Set<String>{'Field1__c', 'Field2__c', 'Field3__c', ... , 'FieldN__c'};

Boolean result = false;
for(String currentField :fieldsSet){
    if(prescriptionRecord.get(currentField) != contactRecord.get(currentField)){
        result = true;
        break;
    }
}

This approach is more elegant than a big 'ol if/else if/else block, but won't be any more efficient (I'd actually expect it to run a bit slower, as object.get(field) is slower than object.field is).

If your fields are different on each object, you could still use that same general idea. Instead of a Set<String> though, you'd build/use a Map<String, String> (or something similar, as long as you're able to say "use fieldX on this object and fieldY on the other object").

The benefit of that approach is that your unit test won't need to test all the possible fields that can differ to get coverage for the entirety of the comparison logic. That said, it would still be a good idea to write separate tests to make sure that each field that can cause a prescription to be revised (instead of renewed) actually does cause your method to return the appropriate result.

Medication changes are very important, which means things that deal with them deserve very thorough testing.

Approach #2

It's basically the same, but this time, we'll keep the fields to be checked in a fieldset. There is documentation on how to use fieldsets in both Visualforce, and Apex. A fieldset is defined from an sobject (for standard objects: setup menu -> customize -> choose your object -> fieldsets.
for custom objects: setup menu -> create -> objects -> select your object, and fieldsets are a related list).

Fieldsets are only manageable through the Classic UI (not through Lightning Experience), and you basically just add fields to it like you would if you were adding the fields to a page layout.

The code ends up being similar

Boolean result = false;
// getFields() from a fieldset returns a List<Schema.FieldSetMember>
// You need to call FieldSetMember.getFieldPath() to get the API name of the field
//   to use it in...pretty much anywhere
for(Schema.FieldSetMember currentFSM :SObjectType.Contact.FieldSets.MyFieldSet.getFields()){
    if(prescriptionRecord.get(currentFSM.getFieldPath()) != contactRecord.get(currentFSM.getFieldPath())){
        result = true;
        break;
    }
}

The benefit to that approach is that it's more declarative (and just a bit shorter, code-wise). Being more declarative, you can change the fields that are compared without requiring a deployment (changes to fieldsets can be made in Production orgs). The test methods you would use for that would be basically the same as the test methods for the first approach.

The drawback is that this requires all fields to have exactly the same names on both objects (though that does also mean it doesn't matter which of your two objects you make the fieldset on). If even one field is different, then you're probably better served by taking the first approach.

1
  • I think I'll have to go w/ Approach #1 (as having matching field names between objects is like, whoa ..) - thanks for the details! Now I just have to find/fight my way back to this project and see how it goes! Commented Oct 13, 2017 at 16:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .