[SalesForce] Unit testing a callout on batch class, I got some differences when using different standard objects

I have a class that is a batch job to upload files to Google Drive using the service's REST API. This particular class used to work only for Attachment objects since it was designed for Salesforce Classic. On Lightning Experience, however, the attachment content is stored on different tables (ContentVersion, to be more accurate). I can still get the content, file name, and other fields in a way that I can mimic the attachment behavior.

My problem is with the test class. The class inserts an account, then an attachment, and then using Test.startTest I call the batch class. The method that uses the Attachment object works fine. However, when running the other method, for ContentDocument, I get the following message right before the callout:

System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

I'm having a difficult time to get to the root of this problem. I literally don't insert/update anything before the callout transaction (the same code runs for the first method, using attachments, after all).

Is there some difference for Content Documents? Some internal trigger, or something like that?

I also tried to use System.runAs() to create the objects first, and then make the callout outside this scope, with Test.startTest() but with no success.

Part of the test class is like this:

Test.startTest();
Test.setMock(HttpCalloutMock.class, new ClienteMock());
GoogleDriveIntegrationBatch batchJob = new GoogleDriveIntegrationBatch();
Database.executeBatch(batchJob, 1);
Test.stopTest();

The setup objects like account, attachment and the custom setting with mock credentials are created/inserted before the Test.startTest() invocation.

Edit

After using the Limits class right before the callout statement, I got the following:

11:51:08.93 (1637716497)|USER_DEBUG|[48]|INFO|DML Rows: 0
11:51:08.93 (1637799554)|USER_DEBUG|[49]|INFO|DML Statements: 0

Best Answer

I found this question/answer when I ran into the same problem. I made a smaller repro, starting from batter.cord's, above. Salesforce support have confirmed that it is a bug and will be fixed, but there is no ETA for the fix. It's not currently on Known Issues, but I'll update if they add it. We have a bug number W-4947848

FYI, my repro follows. The insertContact() test passes, the insertContentVersion() test fails.

@IsTest
private class ContentVersionBugRepro {

    public class MyQueueable implements Queueable, Database.AllowsCallouts {
        public void execute(QueueableContext param1) {
            Http connection = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint(System.Url.getOrgDomainUrl().toExternalForm());
            connection.send(request);
        }
    }

    public class MyMock implements HttpCalloutMock {
        public HttpResponse respond(HttpRequest param1) {
            return new HttpResponse();
        }
    }

    @IsTest static void insertContact() {
        insert new Contact(LastName = 'Test');

        Test.setMock(HttpCalloutMock.class, new MyMock());
        Test.startTest();
        System.enqueueJob(new MyQueueable());
        Test.stopTest();
    }

    @IsTest static void insertContentVersion() {
        insert new ContentVersion(
                VersionData = Blob.valueOf('someData'),
                PathOnClient = 'someData.txt'
        );

        Test.setMock(HttpCalloutMock.class, new MyMock());
        Test.startTest();
        System.enqueueJob(new MyQueueable());
        Test.stopTest();
    }
}
Related Topic