2

In brief, I can't test the value stored in a ContentDocument. Even if the value of the two Blobs is the exact same, equals would return false. Do someone know the reason?

Here's a unit test to check it (comments inline):

@IsTest
static void blobEqualityTest() {
    String content = 'some content';

    Account a = new Account(Name = 'Test Account');
    insert a;

    ContentVersion cv = new ContentVersion(
        VersionData = Blob.valueOf(content),
        Title = 'The Title',
        PathOnClient = 'test.txt'
    );
    insert cv;

    Id contentDocumentId = [select ContentDocumentId from ContentVersion where Id = :cv.Id].ContentDocumentId;

    insert new ContentDocumentLink(
        ContentDocumentId = contentDocumentId,
        LinkedEntityId = a.Id
    );

    ContentDocumentLink[] cdls = [
        select
            ContentDocumentId,
            ContentDocument.Title,
            ContentDocument.LatestPublishedVersion.VersionData,
            ContentDocument.LatestPublishedVersion.Title,
            ContentDocument.LatestPublishedVersion.PathOnClient
        from ContentDocumentLink
        where LinkedEntityId = :a.Id
    ];

    System.assertEquals(1, cdls.size());

    ContentDocumentLink cdl = cdls[0];

    System.assertEquals('The Title', cdl.ContentDocument.Title);
    System.assertEquals('The Title', cdl.ContentDocument.LatestPublishedVersion.Title);

    System.assertEquals(Blob.valueOf('some content'), Blob.valueOf('some content')); // PASS

    Blob expected = Blob.valueOf(content);
    Blob actual = cdl.ContentDocument.LatestPublishedVersion.VersionData;
    System.assertEquals(expected.toString(), actual.toString()); // PASS
    System.assertEquals(EncodingUtil.base64Encode(expected), EncodingUtil.base64Encode(actual)); // PASS
    System.assertEquals(expected, actual); // FAIL: System.AssertException: Assertion Failed: Expected: Blob[12], Actual: Blob[12]
}

As you can see if I create two blobs on the fly the equals work, if I retrieve the blob from the ContentVersion it doesn't, but the toString and base64 values are the same.

The API Version of my classes is 47.0.

4
  • 1
    I don't know what the underlying implementation is, but at a guess I would suggest that equality semantics on a Blob are tied to the underlying storage, and that two Blobs with the same literal string value as content have the same underlying storage pointer. Blobs newly-read from the database would not.
    – David Reed
    Feb 10, 2020 at 15:22
  • Hi @DavidReed, so just to clarify, you think that the Blob's equals doesn't compare the actual values but just the mem pointers? That would explain it, even if it would surprise me! But hey, Salesforce surprises me every day :) Feb 10, 2020 at 15:26
  • 1
    That's my guess. I'm not sure how to prove it, but I'll try some experiments and post an answer if it turns out. It's a reasonable optimization since comparing large Blobs could be quite expensive.
    – David Reed
    Feb 10, 2020 at 15:29
  • 1
    @DavidReed Is pretty easy to prove, see answer.
    – sfdcfox
    Feb 10, 2020 at 15:48

1 Answer 1

3

As guessed in the comments, .equals() behaves as the === (exact equality operator), which generally checks to see if two objects are the exact same object in memory. Instead, you'd want to use == instead, which compares contents.

See:

Object a = Blob.valueOf('hello'), b = Blob.valueOf('hello');
System.debug(a === b); // false
System.debug(a == b);  // true

This is one of those times when you'd want to use System.assert instead of System.assertEquals.

1
  • Hi @sfdcfox, I tried to change the last line of the unit test in example with System.assert(expected == actual);, but it still fails (and the toString and base64 comparisons are just passing fine). Feb 10, 2020 at 17:05

You must log in to answer this question.

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