20

So I have a method that takes as a parameter an Id of a object and uses it to make an external callout. Unfortunately I can't seem to figure out any way to test it.

@isTest static void sometimes_salesforces_sucks() {
   My_Object__c foo = new My_Object__c(Name='test');
   insert foo;
   MyClient client = new MyClient();
   MyMockResponseGenerator mockResponse = new MyMockResponseGenerator();
   Test.setMock(HttpCalloutMock.class,mockResponse);

   Test.startTest();
   client.bar(foo.Id);
   Test.stopTest();
}

This question says that using start/stop test should work, but it doesn't work for me

  • 2
    Can you post your code? When is your callout initiated in code? – greenstork Aug 8 '14 at 22:02
  • 1
    This pattern works for me. Two things you could check: 1) that there are no callouts made as a side effect of any code before Test.startTest; 2) that there are no updates made before the callout within the bar method. (One example of a "hidden" update would be a class that automatically inserts uninitialised custom settings when a setting value is referenced for the first time.) – Keith C Aug 8 '14 at 22:52
  • @greenstork sorry i thought that was obvious. client.bar(); – NSjonas Aug 9 '14 at 0:14
  • 2
    Try to use @isTest(SeeAllData=true), and use an existing foo id (remove the insert of foo from test), if that solve the problem, then the insertion of foo doing something else (maybe a trigger). By the way I have objects that were inserted before the test (like your foo) which didn't had any trigger or something that related to the call out, but yet by insert them the uncommited work pending error produced, if that will be the case the only solution I have found is to use the old method of bypass call out in test --> Test.isRunningtest() (do something else when testing) – GoldenAxe Aug 9 '14 at 18:34
  • 1
    I ended up just bypassing the callout. I'm testing the callout directly in another place so i don't lose any code coverage. just feels hacky... – NSjonas Aug 12 '14 at 20:07
12

You can't do a web service callout after a DML operation in the same transaction. try this code:-

@isTest static void sometimes_salesforces_sucks() {
   My_Object__c foo = new My_Object__c(Name='test');
   insert foo;
   MyClient client = new MyClient();
   Test.startTest();
   Test.setMock(HttpCalloutMock.class,new MyMockResponseGenerator());
   client.bar(foo.Id);
   Test.stopTest();
}

You cannot perform a DML operation prior to a callout. All the DML operations should be invoked only after you are done with callouts.So, make a webservice callout first and then save the request.

If you are making multiple callouts, then save all the requests in a list or map and post callouts you save them.

Basically the following scenario will work :-

query callout query callout insert

callout callout callout insert or update

But the following scenario will fail :-

callout insert callout <---- fails="" here="" p=""> Possible workaround :-

splits the transaction into two separate Ajax processes. The first inserts the record and the second performs the callout and is able to update the newly inserted record.

  • 1
    You comment and your code are a bit conflicting. You are correct but you need to add why your code works so the OP can understand it. – Eric Sep 18 '15 at 6:38
  • I'm having the same problem. One way it doesn't fail - comment out static initialization section, where I make a bunch of insert/update DMLs. Also, my test is the same as @NSjonas has. Surrounded by Test.startTest()/Test.endTest(). – Andrii Muzychuk Jun 22 '16 at 17:51
  • This isn't always true, I've got loads of tests that make callouts with a mocked response, and those can happen after I've inserted test data. – Ralph Callaway May 3 '18 at 3:22
1

Note to self (because I know I will forget it in the future, and I hope I will find this post again) : You can also encounter that error when an email is sent after record insert/update.

Don't forget to add a bypass set to true only during test executions.

  • did you know you can star questions so you can dig them up later? – NSjonas Sep 21 '16 at 1:04
1

I got the same issue. Fixed it by creating Mock Data within Test.StartTest and Test.StopTest. Note: Test.LoadData is inserting test data from Static Resource CSV.

    List<Account> accList=Test.loadData(Account.sObjectType,'challengeAccounts');AccountListController controller = new AccountListController();     
    Test.startTest();
    // Set mock callout class
    Test.setMock( HttpCalloutMock.class, new OpportunityMockHttpResponseGenerator() ); 

    controller.GetOpportunityList();
    Test.stopTest();
0

Got the same error. The Test.setMock() had to be immediately after the Test.startTest() to fix it and the DML operations were done prior to Test.startTest().

0

At the moment of writing (June 2019), unit tests are not always working as expected when it comes to libraries/folders/files. Code running fine 'live' may throw exceptions like that in unit tests.

What is probably going on, my guess, is that deep under water storage is not fully in the database but also partly on a file system. Then, a lot of magic happens with ContentXxxxx objects to make it appear as if we deal with standard SObjects. Think of creation of a ContentDocument on insert of a ContentVersion, or creation of a ContentFolderMember to the root folder of a library if you create a new ContentDocumentLink.

Creating those 4 objects does not work in a unit test though.

Things you try in the unit tests (which have to be rolled back after the test) may not behave the same as in actual execution. It may save you a lot of time to isolate the ContentXxxxx functionality, and leave it in the 25% untested code Salesforce allows you. The test outcome you get is not assured to be representative of the production behavior at the moment yet. Just make sure it works fine by other means.

It may sound a bit negative, but then I just spend two days too trying to work around the issues. :-)

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.