1

There are a lot of questions related to "System.CalloutException: You have uncommitted work pending" errors, but could not find solution for following use case:

Basically, a static method in the test class inserts a test user, a custom settings record, account and opportunity records. Then the test methods run as the testing user and covers code for a queueable apex that executes an HTTP callout.

Here is the code:

@isTest
private class ProjectCalloutServiceTest {
    static List<Opportunity> oppLst;
    static Account acc;
    static User adminUser;

    static{ 
        Id admProfId = TestFactory.getProfileAdminId();
        adminUser = TestFactory.createUser('FirstAdmin',admProfId);
        insert adminUser;

        System.runAs(adminUser){
            //create the Custom Settings
            ServiceTokens__c servToken = new ServiceTokens__c(
                                        Name = 'ProjectServiceToken'
                                        ,Token__c = 'DUMMY-TOKEN');
            insert servToken;

            //create account
            Account acc = TestFactory.createAccount('Account 1');
            insert acc;

            //create oppty
            oppLst = new List<Opportunity>{
                    TestFactory.createOpportunity('Project1',acc.Id, 'New project'
                        ,'Submitted Project', Date.newInstance(2019, 11, 17), 17000),
                    TestFactory.createOpportunity('Project2',acc.Id, 'New project'
                        ,'Resubmit Project', Date.newInstance(2019, 12, 17), 17000)
                    };
            insert oppLst;
        }
    }

    @isTest static void testCalloutSuccess() {
        System.runAs(adminUser){
            Test.startTest();
                // Set mock callout class 
                Test.setMock(HttpCalloutMock.class, new ProjectCalloutServiceMock()); 
                ProjectCalloutService.postOpportunityToPMS(new List<Id>{oppLst[0].Id});
            Test.stopTest();  

            Opportunity opp = [select StageName from Opportunity where id =:oppLst[0].Id];
            System.assertEquals('Submitted Project',opp.StageName);
        }
    }

    @isTest static void testCalloutFailure() {
        System.runAs(adminUser){
            Test.startTest();
                // Set mock callout class 
                Test.setMock(HttpCalloutMock.class, new ProjectCalloutServiceMockFailure()); 
                ProjectCalloutService.postOpportunityToPMS(new List<Id>{oppLst[1].Id});        
            Test.stopTest();   
            Opportunity opp = [select StageName from Opportunity where id =: oppLst[1].Id];
            System.assertEquals('Resubmit Project',opp.StageName);
        }
    }

}

When running the above test class, both test method fail with uncommitted work callout exception below:

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

However, if only the lines of code for test user creation and system.runas is commented then the test class executes successfully as the running user.

It is recommended to run test methods as a testing user, but then how do we insert the test user in the test class and prevent the uncommitted work error ?

This known issue is similar but it has been fixed since a long time now so cannot understand what is causing the callout exception.

Any help is appreciated, thanks

  • if you want to create testusers, do that within a system.runAs([select ID from user where id = : UserInfo.getUserId()][0]) {..} – cropredy Apr 25 '18 at 16:28
  • It does look like that the known issue mentioned here still persists. I tried creating a new user in the test class and got the same exception. However if you run as any existing user (by querying for an existing user), it works fine. – Jayant Das Apr 25 '18 at 17:26
  • thanks for your comments. >cropredy create test user like you suggested did not work unfortunately... @JayantDas without creating test user and doing run as an existing user works indeed, but it is strange why test user cannot be inserted. do I have to log a case to request Salesforce support to look into the known issue again ? – Shamina Apr 25 '18 at 18:52
  • You may like to reach out to Salesforce with reference to the old issue you have mentioned. I couldn't really find a way to get past of this issue. – Jayant Das Apr 25 '18 at 18:53
1

There were several Salesforce issues related to

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

Besides of the issues mentioned, additional issues that I have encountered with, were:

  1. The problem with @future(Callout=true) methods. First time I reported this was at February 12, 2015 Spring 15 Release (v.33)

  2. There was a known issue, which was claimed to be fixed in the next year, however, it wasn't really fixed. I have opened another Salesforce support case, they told me the work id of it was "W-3171561" but they never provided a list to known issue and I was promised to be notified once this issue is resolved, however, I was never notified on this issue resolution.

  3. Later I suggested a workaround for this long-running issue to downgrade the apex class version to 28.

  4. As of Summer 2019 release (version 46) I can't reproduce the future callout issue anymore. However, I have found another issue, the reasons for which are not clear to me. It is somehow related to user trigger and batch or queueable jobs. I have opened another Salesforce support case for this. Let's see how this goes. I have posted about this issue today on my blog

  5. The issue that you mention seems to be fixed for future jobs but not for queueable jobs.

Conclusion.

There is some constant long-running ever-lasting bug related to uncommitted work pending. It is hard to find and reproduce it. Every time when Salesforce makes some change to this implementation, it becomes harder to debug and to troubleshoot it, but the core issue is still present.

Some workaround might be applicable for this issue.

  1. Try to downgrade the class version to 28.0 or older where issue wasn't introduced yet (the version should be older than Spring 15).

  2. Try to move the setup code into method annotated with @testSetup annotation. It is not clear why, however, in many cases this helps to workaround this issue

  • Thanks a lot for this consolidated research. If I remember correctly, I posted this question when trying this test class for a trailhead module and I finally skipped creating the test user in test data to be able to pass the module. Its sad to know that it is still a known issue and not completed resolved. based on your analysis, I prefer the @testSetup workaround and will try it next time on such test class. thanks again – Shamina Jul 10 at 17:45

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.