2

SOLUTION An object has workflow rule that sends an email and it was not mocked

EDIT I am simply trying to test future method that invokes SOAP service, but I get an exception.

EDIT 2 I have @testSetup method. If I comment it out, there is no exception. But Test.startTest() has to add new context to test, so no previous data can influence further execution?

class FutureClass {
    @future(callout=true)
    public static void invokeFuture() {
            Webservice.doSomeSOAPrequest(); // <- Exception appears at this line, this invokes webservice method.
        }

    }
}

@isTest
class BikesWebserviceTest {
    @isTest
    static void testSomeWSOperations() {
        // No queries for now.
        Test.startTest();
        Test.setMock(WebServiceMock.class, new BikeCalloutMock());

        FutureClass.invokeFuture(); // simply call future method that invokes SOAP service

        Test.stopTest();
        // no assertions for now
    }
}

public class BikeCalloutMock implements WebServiceMock {
    public void doInvoke(
            Object stub,
            Object request_x,
            Map<String, Object> response_map_x,
            String endpoint,
            String soapAction,
            String requestName,
            String responseNS,
            String responseName,
            String responseType) {

        BikeWebservice.ResponseElement responseElement = new BikeWebservice.ResponseElement();
        responseElement.someField = 1;
        response_map_x.put('response_x', responseElement);
    }
}
  • Is there any DML in BikeCalloutMock? – Oles Malkov Jun 10 '16 at 15:58
  • @OlesMalkov no, it returns only an object the same as real webservice – Vladyslav K Jun 10 '16 at 16:08
  • Then you probably need to set up a debug log and check all your DML there. – Oles Malkov Jun 10 '16 at 16:12
  • you can also take a look at the Dan Appleman Advanced Apex 3rd edition chapter 7 pattern on async to see how a custom asyncRequest__c object and queueables can be used in a way that avoids this problem. Your future method will run into governor issues with bikes of size > 100. The Appleman pattern also solves this Limits issue. – cropredy Jun 11 '16 at 1:08
  • @cropredy I have created mechanism to cut list every 100 elements, so I future will not get more that 100. Unfortunately I have only 1 edition of this book. – Vladyslav K Jun 13 '16 at 10:28
4
+50

It may work during live and record get updated but according to your code as posted you should be getting future methods cannot call future methods so I believe we do not have a clear picture of what is actually going on.

Your future updates the sObject which fires the trigger which calls the future again.

Try changing

FutureClass.invokeFuture(JSON.serialize(bikes));

to

if(!system.isFuture())
    FutureClass.invokeFuture(JSON.serialize(bikes));

Another thing to be aware of is that email alerts will cause the Uncommitted work pending error is test methods as well so check into those

Issue with Email Alerts and Test.setMock - Uncommitted Work Pending

| improve this answer | |
  • I checked criteria for running future method, it runs only once. I can even remove dml update in future method, anyway an exception is thrown when calling out – Vladyslav K Jun 13 '16 at 14:29
  • According to what u posted it runs twice. Check for email alerts as well. See the link I posted – Eric Jun 13 '16 at 14:34
  • You were correct, my apologies, I just did not read attentively your last lines of answer – Vladyslav K Jun 15 '16 at 17:01
1

the problem is that you are performing a DML which will fire a @future which will fire a callout.

In normal world that would be 2 separate transactions - DML in sync Apex and Callout in @future context.

However, in test, it is 1 transaction, therefore this exception is thrown.

you can refer to this article about MOCKS after DML in test, but the problem is, you are performing DML and a callout in one single line.

update bike;

The only way to avoid exception to be thrown in this particular scenario is, probably, use test.isrunningtest() in your actual class, block callout method call in test context, call it in test directly after data is set.

| improve this answer | |
  • Please have a look at edit 1 – Vladyslav K Jun 13 '16 at 12:59
  • read the article about MOCKS and DML I mentioned, it directly describes your situation. Also, try to check for limits after test.stoptest() I think you need to disable future execution for test context and/or disable callout itself with help or test.isrunningtest() and than call it directly, as described in article. – Andrei.Z Jun 13 '16 at 15:51

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.