7

When running unit tests, I'm currently getting:

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

The issue is that I have a trigger that runs a callout after account update. So I need to set the mock callout before anything would update the account. The current test works if I comment out the second custom object logic (i.e. the second line would cause the trigger to fire again). However, if I send update accounts two separate times, it throws the above error.

My current approach is the following:

static testMethod void testNow() {
    Test.startTest();
    Test.setMock(HttpCalloutMock.class, new ThisIsMyMockCallout());

    List<Account> testAccs = makeNewAccounts(1,10); // Makes list of 1 account
    insert testAccs; // This works without mock since it doesn't update acc

    List<CustomObject> customObjs = makeNewCustomObs(testAccs);
    insert customObjs; // This needs mock as it updates account due to relation

    List<CustomObjectA> customObjsA = newMakeCustomObsA(customObjs,testAccs);
    insert customObjsA; // This needs mock as it updates account due to relation
    Test.stopTest();
    // A bunch of assertEquals tests
}

Any help would be great!

  • 1
    Did you try MultiMock ? or can the test be broken into two testmethods? – cropredy Dec 24 '15 at 17:19
  • @crop1645 MultiMock seems to be for when there are multiple response bodies; whereas, the trello callout just posts data to external site every time there is an updated account, so no need for multiple responses. Is this correct? – williamrfry Dec 24 '15 at 20:21
3

It appears to me, the issue that you're having is because of where you've located Test.startTest() in your test method. As I understand your description, you're still creating test data at the beginning of your method.

static testMethod void testNow() {

    Test.setMock(HttpCalloutMock.class, new ThisIsMyMockCallout());

    List<Account> testAccs = makeNewAccounts(1,10); // Makes list of 1 account
    insert testAccs; // This works without mock since it doesn't update acc

    Test.startTest();
    List<CustomObject> customObjs = makeNewCustomObs(testAccs);
    insert customObjs; // This needs mock as it updates account due to relation

    List<CustomObjectA> customObjsA = newMakeCustomObsA(testAccs);
    insert customObjsA; // This needs mock as it updates account due to relation
    Test.stopTest();
    // A bunch of assertEquals tests
}

That having been said, it also appears that the last two lines, you reference need the mock because they update the relation and fire the trigger. Each would fire the trigger separately. If that's the case, you would presumably use two methods to cover the testing of your trigger; one for each of these. The first method would cover the insert of customOjbs and the 2nd method would cover the insert of customObsA.

The above assumes they don't need to be in the order you have them in your test method. If they do, then I believe you'd need to begin Test.startTest in your final test class after the insert of customObjs rather than before, using the mock as a response to it before the insert of the final list of records.

  • I tried repositioning the startTest() to no avail. I updated the simplified code above. The second custom object actually requires the first to be inserted, can I still split across two methods if this is the case? – williamrfry Dec 29 '15 at 16:56
  • You still should have two test methods. The first one should only cover the 1st insert and the mock response for that insert. The 2nd one should allow the mock response to the 1st insert to occur after which begin your test for the mock response to the 2nd insert. The Test.startTest() should be moved to the location of the 2nd insert in the 2nd method and would remain as you show now for the 1st. I hope that makes sense. – crmprogdev Dec 29 '15 at 17:16
  • Should the second method call the first method before startTest()? Since test methods cannot return anything, how does the second method get access to the records inserted in the first method? Sorry for the noob questions.. – williamrfry Dec 29 '15 at 17:45
  • Everything that's in the first method is repeated in the second method except that test.startTest is moved to the new location. It doesn't call the 1st method, it just calls the same code since it needs those records, but doesn't do it until the test begins. They'll be contained in the isolated test environment (assuming you're not using seeAllData) where they were created. You'll have MytestMethod1 and MytestMethod2. – crmprogdev Dec 29 '15 at 18:56

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.