1

I have methods in my class that return a single item from a SOQL using the items Id.

        return [SELECT Id, OwnerId, IsDeleted, Name, CreatedDate, LastModifiedDate, CreatedById, LastModifiedById, SystemModstamp, TouchpointDev__PersonalItem__c, TouchpointDev__AssignedTo__r.Name, TouchpointDev__Completed__c, TouchpointDev__CompletedBy__r.Name, TouchpointDev__Completed_Date__c, TouchpointDev__High_Priority__c, TouchpointDev__Display__c, TouchpointDev__Item_Due_Date__c, TouchpointDev__Activities__c, TouchpointDev__Past_Due__c, TouchpointDev__Trashed__c 
            FROM TouchpointDev__Whiteboard_Item__c
            WHERE Id =: whiteboardItem.Id];

Changing the return value from the argument used in the method, it now breaks all my test classes. Even though the values all match, they are just out of of order and also have some extra fields that belong to the object.

How would I make my test pass?

System.AssertException: Assertion Failed:

Expected: TouchpointDev__Whiteboard_Item__c:{Name=Test Item 1, TouchpointDev__Item_Due_Date__c=2016-08-18 00:00:00, TouchpointDev__AssignedTo__c=005d0000006FYTqAAO, TouchpointDev__High_Priority__c=false, TouchpointDev__PersonalItem__c=true, TouchpointDev__Completed__c=false, Id=a00d000000k2s8ZAAQ},

Actual: TouchpointDev__Whiteboard_Item__c:{Id=a00d000000k2s8ZAAQ, OwnerId=005d0000005NR4WAAW, IsDeleted=false, Name=Test Item 1, CreatedDate=2016-08-18 21:46:09, LastModifiedDate=2016-08-18 21:46:09, CreatedById=005d0000005NR4WAAW, LastModifiedById=005d0000005NR4WAAW, SystemModstamp=2016-08-18 21:46:09, TouchpointDev__PersonalItem__c=true, TouchpointDev__AssignedTo__c=005d0000006FYTqAAO, TouchpointDev__Completed__c=false, TouchpointDev__High_Priority__c=false, TouchpointDev__Display__c=false, TouchpointDev__Item_Due_Date__c=2016-08-18 00:00:00, TouchpointDev__Activities__c=0, TouchpointDev__Past_Due__c=false, TouchpointDev__Trashed__c=false}

  • How is your assert coded? – cropredy Aug 18 '16 at 22:03
  • System.assertEquals(item1, WhiteboardItemsApexController.updateWhiteboardItemDB(item1)); – Tyler Zika Aug 18 '16 at 22:16
  • switching the order will not make it pass. you need to figure out why the values are not as expected – Eric Aug 18 '16 at 22:18
  • @Eric That's why I'm here :D – Tyler Zika Aug 18 '16 at 22:41
  • 2
    Don't assert SObject equality. At best, it's silly. It's certainly not a tenable strategy in most cases. – Adrian Larson Aug 18 '16 at 22:42
3

Asserting SObject equality can be tricky as you have noted - especially with system-generated values such as auto-number fields or audit fields.

One pattern (and there are many - hopefully others will jump in with their favorites) would be

set<Schema.SobjectField> fldsToVerify = new set<Schema.SobjectField> {
     Foo__c.Fld1__c, Foo__c.Fld2__c, ....};

Foo__c expected = new Foo__c(Fld1__c = 'bar', Fld2__c = 10);
Foo__c actual = [select id, Fld1__c, Fld2__c from Foo__c where ...];
for (Schema.SobjectField fld : fldsToVerify)
  System.assertEquals(expected.get(fld),actual.get(fld),fld + ' verify error');

I blogged a fluent pattern as well that was inspired by others

3

I see from your comments you are doing something like:

System.assertEquals(item1, WhiteboardItemsApexController.updateWhiteboardItemDB(item1))‌​;

Both item1 and the sObject returned from updateWhiteboardItemDB() are of the type TouchpointDev__Whiteboard_Item__c.

As others have commented (hat tip to Adrian), you are asking Apex to compare two instances of TouchpointDev__Whiteboard_Item__c for sObject equality.

From the documentation for the == operator:

For sObjects and sObject arrays, == performs a deep check of all sObject field values before returning its result. Likewise for collections and built-in Apex objects.

That is, it is going to look at every field that is loaded within each sObject and compare them for equality. If they all match then the sObjects are the same.

In your question, you can see that each instance of TouchpointDev__Whiteboard_Item__c has a different set of fields loaded. The one you retrieved via SOQL has the OwnerId, and IsDeleted fields defined where as the original item1 does not. Hence they are not equal as far as Apex is concerned.

This concept of sObject equality is also mirrored in the hashCode() implementation. You will only get the same hash if both sObjects have all the same field values.


You should narrow down your test case assertion(s) to what you care about being equal at a field level.

This could be as simple as checking the ID's on both records are the same. Or there might be additional fields that you want to confirm are the same.


If you want to do something more elaborate, you could look at using the getPopulatedFieldsAsMap() function that came out in Summer '16. It could be used to do a field by field level comparison between two sObjects that ignores and fields that aren't present in both. The utility of such a comparison will depend on exactly what you are trying to do.

  • 1
    Perhaps the getPopulatedFieldsAsMap() method would be most useful if added to a Set and combined with some sort of retainAll call. – Adrian Larson Aug 18 '16 at 23:34
  • @AdrianLarson What is this retainAll that you speak of? Is that a Java thing? Ah, Google tells me it is the intersection of two Sets. So you would know how the two sObjects differ. Could build up a useful sObject comparison class to find differences, load missing fields, compare for a named list of fields. – Daniel Ballinger Aug 18 '16 at 23:42

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.