2

So I'm familiar with the error

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

but I'm stumped with this one! I have a call with a @future method that makes a callout then implements some DML, which is called by a trigger whenever Cases are inserted. Thing is, when testing in sandbox everything works fine - it's only during a deployment that the error is returned.

My class w/ @future method:

public with sharing class Case_Handler {

@future (callout=true)
static public void CurrencyConversion(set<ID> caseIDList) {

    Http http = new http();
    HttpRequest req = new HttpRequest();
    HttpResponse res = new HttpResponse();

    req.setEndpoint('https://openexchangerates.org/api/latest.json?app_id=27f541f3a34b4cafa91105ac17d9ca8f');
    req.setTimeout(60000);
    req.setMethod('GET');

    res = http.send(req);

    system.debug(res.getbody());

    list<Case> caseList = [SELECT CreatedDate, Revenue__c, Currency__c, Revenue_Original_Currency__c FROM Case WHERE ID IN :caseIDList];
    list<Currency_Exchange_Rates__c> cerList = new list<Currency_Exchange_Rates__c>();

    for(Case c : caseList){ **DML Statements happen in here**
    etc...

 

My trigger

trigger Case_CurrencyConversion on Case (after insert) {

    Case_Handler.CurrencyConversion(trigger.newMap.keySet());

}

 

My test class

@isTest
private class Case_CurrencyConversion_Test implements HttpCalloutMock{

    public HTTPResponse respond(HTTPRequest req) {
        //System.assertEquals('http://api.salesforce.com/foo/bar', req.getEndpoint());
        //System.assertEquals('GET', req.getMethod());

        // Create a fake response
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');

        res.setBody('{"disclaimer":"Exchange rates are provided for informational purposes only, and do not constitute financial advice of any kind. Although every attempt is made to ensure quality, NO guarantees are given whatsoever of accuracy, validity, availability, or fitness for any purpose - please use at your own risk. All usage is subject to your acceptance of the Terms and Conditions of Service, available at: https://openexchangerates.org/terms/","license":"Data sourced from various providers with public-facing APIs; copyright may apply; resale is prohibited; no warranties given of any kind. Bitcoin data provided by http://coindesk.com. All usage is subject to your acceptance of the License Agreement available at: https://openexchangerates.org/license/","timestamp":1427400062,"base":"USD","rates":{"AED":3.67287,"AFN":57.743533,"ALL":128.1274,"AMD":473.356002,"ANG":1.78952,"AOA":107.534251,"ARS":8.801325,"AUD":1.279481,"AWG":1.795,"AZN":1.048575,"BAM":1.795899,"BBD":2,"BDT":77.85953,"BGN":1.795321,"BHD":0.377012,"BIF":1560.42,"BMD":1,"BND":1.368998,"BOB":6.90733,"BRL":3.179781,"BSD":1,"BTC":0.0039544401,"BTN":62.587816,"BWP":9.81483,"BYR":14553.233333,"BZD":1.997243,"CAD":1.248137,"CDF":927.1845,"CHF":0.962274,"CLF":0.024602,"CLP":622.242199,"CNY":6.192248,"COP":2559.085039,"CRC":533.170305,"CUC":1,"CUP":0.99985,"CVE":100.754374,"CZK":25.21538,"DJF":177.6412,"DKK":6.855725,"DOP":44.69114,"DZD":96.622469,"EEK":14.337525,"EGP":7.627253,"ERN":15.114767,"ETB":20.40937,"EUR":0.919121,"FJD":2.035896,"FKP":0.673526,"GBP":0.673526,"GEL":2.222588,"GGP":0.673526,"GHS":3.792465,"GIP":0.673526,"GMD":43.01859,"GNF":7296.384922,"GTQ":7.63688,"GYD":206.109002,"HKD":7.755124,"HNL":21.49481,"HRK":7.021803,"HTG":47.3988,"HUF":275.243098,"IDR":12993.883333,"ILS":3.954727,"IMP":0.673526,"INR":62.7263,"IQD":1184.276667,"IRR":27892.666667,"ISK":135.853,"JEP":0.673526,"JMD":114.824601,"JOD":0.708552,"JPY":119.317001,"KES":92.058811,"KGS":63.037501,"KHR":4018.81665,"KMF":451.362695,"KPW":899.91,"KRW":1105.141675,"KWD":0.299766,"KYD":0.820343,"KZT":185.8425,"LAK":8113.140065,"LBP":1507.126667,"LKR":132.9905,"LRD":84.553335,"LSL":11.94438,"LTL":2.933767,"LVL":0.644846,"LYD":1.369931,"MAD":9.854486,"MDL":18.52518,"MGA":3007.028366,"MKD":56.5572,"MMK":1046.1125,"MNT":1985.833333,"MOP":7.969258,"MRO":315,"MTL":0.394649,"MUR":36.37293,"MVR":15.258838,"MWK":443.832299,"MXN":15.09305,"MYR":3.671121,"MZN":35.7355,"NAD":11.94438,"NGN":199.099701,"NIO":26.78082,"NOK":7.894581,"NPR":99.98331,"NZD":1.320448,"OMR":0.385011,"PAB":1,"PEN":3.078775,"PGK":2.661019,"PHP":44.82448,"PKR":101.973999,"PLN":3.750491,"PYG":4810.941589,"QAR":3.641241,"RON":4.061622,"RSD":110.780999,"RUB":57.39713,"RWF":690.4415,"SAR":3.750843,"SBD":7.716739,"SCR":13.71065,"SDG":5.958921,"SEK":8.564549,"SGD":1.370576,"SHP":0.673526,"SLL":4345,"SOS":700.086302,"SRD":3.2825,"STD":22432.083333,"SVC":8.72256,"SYP":211.78,"SZL":11.94438,"THB":32.54761,"TJS":5.7148,"TMT":3.499933,"TND":1.943006,"TOP":2.025876,"TRY":2.59737,"TTD":6.348941,"TWD":31.25631,"TZS":1841.041667,"UAH":21.758274,"UGX":2968.59,"USD":1,"UYU":25.48513,"UZS":2487.383301,"VEF":6.31842,"VND":21511.666667,"VUV":105.348334,"WST":2.472739,"XAF":601.660469,"XAG":0.0585635,"XAU":0.00083129,"XCD":2.70102,"XDR":0.719741,"XOF":601.109009,"XPF":109.74309,"YER":215.0641,"ZAR":11.94981,"ZMK":5253.075255,"ZMW":7.586915,"ZWL":322.355006}}');

        res.setStatusCode(200);

        return res;
    }


    static testmethod void test_method_one() {
        // Implement test code

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

        list<Case> cases = new list<Case>{new Case(Revenue_Original_Currency__c = 100,
                                                   Currency__c = 'GBP'),
                                          new Case(Revenue_Original_Currency__c = 100,
                                                   Currency__c = 'USD'),
                                          new Case(Revenue_Original_Currency__c = 200,
                                                   Currency__c = 'GBP'),
                                          new Case(Revenue_Original_Currency__c = 100)};


        Test.startTest(); 
        insert cases;
        Test.stopTest();
    ... 
    test assertions, etc...

 

EDIT: In response to the suggestion that I separate the testing of the trigger and the @future call, here is the new code I tried but received the same issue:

static testmethod void test_method_one() {
    // Implement test code

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

    list<Case> cases = new list<Case>{new Case(Revenue_Original_Currency__c = 100,
                                               Currency__c = 'GBP'),
                                      new Case(Revenue_Original_Currency__c = 100,
                                               Currency__c = 'USD'),
                                      new Case(Revenue_Original_Currency__c = 200,
                                               Currency__c = 'GBP'),
                                      new Case(Revenue_Original_Currency__c = 100)};

    insert cases;

    set<ID> caseIDs = new set<ID>();
    for(Case c : cases) caseIDs.add(c.ID);

    Test.startTest(); 
    Case_Handler.CurrencyConversion(caseIDs);
    Test.stopTest();
6
  • 1
  • 1
    The problem is that the Test.startTest() and stopTest() are flattening the future callout and trigger into the same transaction. You will need to find a way to isolate the various parts (trigger, future method, callout) in a test context. Jun 2, 2015 at 21:23
  • @Daniel, I did try separating them into two different testmethods. When I wrote a test that didn't fire the trigger but instead passed the case IDs directly to the future method, it tested fine but I received the same error when trying to deploy.
    – smohyee
    Jun 2, 2015 at 21:40
  • Also I don't think that this is a duplicate of your linked issue, as his problem was a lack of test coverage. I'm getting full test coverage, and its testing correctly in the sandbox, its upon deploy to production that I receive the error.
    – smohyee
    Jun 2, 2015 at 21:58
  • @DanielBallinger Your comment seems to have flagged my post as a duplicate question, but I believe you are incorrect. Can the flag be removed so more people will consider my question?
    – smohyee
    Jun 3, 2015 at 19:18

2 Answers 2

1

As Daniel said into his comment, you will need to separate them into 2 different test methods. But you will also need to change your trigger to not call your future call

if(Test.isRunningTest() == false) Case_Handler.CurrencyConversion(trigger.newMap.keySet());
1
  • I have done this, but it didn't make a difference in terms of receiving the error. I created a second test method that fired the trigger (and yes, I used test.isrunningtest to ensure that the future method wasn't called for that test method). And for my original test I changed the code to pass IDs to the future method directly, rather than via a trigger (that code I've added as an edit to my main post). But I still get the error upon attempting to deploy!
    – smohyee
    Jun 3, 2015 at 16:54
1

Does the org you are deploying to have additional triggers, workflow, etc... on Cases that may be firing when you call Test.stopTest() during the deployment test execution?

You could test this by trying to deploy just a test class that inserts a Case within the Test.startTest() and Test.stopTest() calls. If that fails in isolation there is something in the target org already trying to make a callout.

If it is a managed package making the callout, look for a custom setting that could temporarily disable the trigger functionality.

1
  • That's a fair point, though the error message seems to highlight my new test class as the culprit. I shall run a test class that only inserts a case and report back the results.
    – smohyee
    Jun 5, 2015 at 18:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .