2

I have almost tried all solutions available on internet but i still face the error. my Test class code is

@isTest
private class RegisterTest {
@isTest static void existingUserCheck()  {                
    Registration__c checkInsert = new Registration__c(First_Name__c = 'Nilkamal', Last_Name__c = 'Gotarne', Company__c = 'Xoyal It Services',
                                                      Job_Title__c = 'Salesforce Developer', Email__c = '[email protected]', Phone__c = '+917020111572',
                                                      Address_Line_1__c = 'Bund garden, pune 411015', Address_Line_2__c = '', City__c = 'Pune',
                                                      State__c = 'Maharashtra', Country__c = 'India', Zip__c = '411001');    

    insert checkInsert;

    Register CheckRegister = new Register(new ApexPages.StandardController(new Registration__c()));       
    CheckRegister.RegData.First_Name__c = 'Nilkamal';
    CheckRegister.RegData.Last_Name__c = 'Gotarne';
    CheckRegister.RegData.Email__c = '[email protected]';
    CheckRegister.RegData.Phone__c = '9021567897';
    CheckRegister.RegData.Address_Line_1__c = 'Earth';
    CheckRegister.RegData.Address_Line_2__c = 'Mars' ;
    CheckRegister.RegData.Company__c = 'Xoya';
    CheckRegister.RegData.Job_Title__c = 'Sales Man';
    CheckRegister.RegData.Zip__c = '411015';
    CheckRegister.RegData.City__c = 'Poona';
    CheckRegister.RegData.State__c = 'Maha Rashtra';
    CheckRegister.RegData.Country__c = 'Bharat';    
    Test.startTest();        
    Test.setMock(HttpCalloutMock.class, new MockHttpResponseRegisterInfo());                    
    CheckRegister.saveUserInfo();
    Test.stopTest();        
}
}

My class is

public class Register {
public final Registration__c RegData;
public PageReference saveUserInfo() {                   
   if([Select First_Name__c, Last_Name__c, Company__c, Job_Title__c, Email__c, Phone__c, Address_Line_1__c, Address_Line_2__c, City__c, State__c, Country__c, Zip__c, Total_fund__c From Registration__c Limit 1].size() == 0)
    {            
        Registration__c createRegInfo = new Registration__c(First_Name__c = RegData.First_Name__c, Last_Name__c = RegData.Last_Name__c, Email__c = RegData.Email__c,
                                                           Phone__c = RegData.Phone__c, Address_Line_1__c = RegData.Address_Line_1__c, Address_Line_2__c = RegData.Address_Line_2__c,
                                                           Company__c = RegData.Company__c, Job_Title__c = RegData.Job_Title__c, Zip__c = RegData.Zip__c, City__c = RegData.City__c,
                                                           State__c = RegData.State__c, Country__c = RegData.Country__c);                        

        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('http://twil-smsapp.herokuapp.com/api/createRegInfo');
        request.setMethod('POST');
        request.setTimeout(10000);
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        // Set the body as a JSON object
        request.setBody('{"regInfo":'+JSON.serialize(createRegInfo)+',"organisationId":"'+UserInfo.getOrganizationId()+'" ,"createdBy":"'+userInfo.getUserId()+'"}');
        //System.debug('{"contactInfo":'+JSON.serialize(contact)+',"message":'+JSON.serialize(mess.Text_Message__c)+',"templateId":"'+template_id+'","createdBy":"'+userInfo.getUserId()+'"}');
        HttpResponse response = http.send(request);
        System.debug('{"regInfo":'+JSON.serialize(createRegInfo)+',"organisationId":'+UserInfo.getOrganizationId()+' ,"createdBy":"'+userInfo.getUserId()+'"}');
        // Parse the JSON response
        if (response.getStatusCode() != 200) {
            System.debug(response.getBody());
            System.debug('The status code returned was not expected: ' +
                response.getStatusCode() + ' ' + response.getStatus());
            ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Oops, something went wrong!!'));
        } else {

            insert createRegInfo;
            ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM,'Registered Successfully you can also update below information!'));
            System.debug(response.getBody());
        }

    } else {
        Registration__c createRegInfo = [Select First_Name__c, Last_Name__c, Company__c, Job_Title__c, Email__c, Phone__c, Address_Line_1__c,
                                         Address_Line_2__c, City__c, State__c, Country__c, Zip__c, Total_fund__c FROM Registration__c LIMIT 1 FOR UPDATE];
            createRegInfo.First_Name__c = RegData.First_Name__c;
            createRegInfo.Last_Name__c = RegData.Last_Name__c;
            createRegInfo.Email__c = RegData.Email__c;
            createRegInfo.Phone__c = RegData.Phone__c;
            createRegInfo.Address_Line_1__c = RegData.Address_Line_1__c;
            createRegInfo.Address_Line_2__c = RegData.Address_Line_2__c;
            createRegInfo.Company__c = RegData.Company__c;
            createRegInfo.Job_Title__c = RegData.Job_Title__c;
            createRegInfo.Zip__c = RegData.Zip__c;
            createRegInfo.City__c = RegData.City__c;
            createRegInfo.State__c = RegData.State__c;
            createRegInfo.Country__c = RegData.Country__c;
        update createRegInfo;                      

        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('http://twil-smsapp.herokuapp.com/api/createRegInfo');
        request.setMethod('POST');
        request.setTimeout(10000);
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        // Set the body as a JSON object
        request.setBody('{"regInfo":'+JSON.serialize(createRegInfo)+',"organisationId":"'+UserInfo.getOrganizationId()+'" ,"createdBy":"'+userInfo.getUserId()+'"}');
        //System.debug('{"contactInfo":'+JSON.serialize(contact)+',"message":'+JSON.serialize(mess.Text_Message__c)+',"templateId":"'+template_id+'","createdBy":"'+userInfo.getUserId()+'"}');
        HttpResponse response = http.send(request);
        System.debug('{"regInfo":'+JSON.serialize(createRegInfo)+',"organisationId":'+UserInfo.getOrganizationId()+' ,"createdBy":"'+userInfo.getUserId()+'"}');
        // Parse the JSON response
        if (response.getStatusCode() != 200) {
            System.debug(response.getBody());
            System.debug('The status code returned was not expected: ' +
                response.getStatusCode() + ' ' + response.getStatus());
            ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Oops, something went wrong!!'));
        } else {
            insert createRegInfo;
            ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM,'Registered Successfully!'));
            System.debug(response.getBody());
        }
    }
    //System.debug(regInfo);
    return null;
}
}

Mock callout class

@isTest
global class MockHttpResponseRegisterInfo implements HttpCalloutMock {
// Implement this interface method
global HTTPResponse respond(HTTPRequest req) {
    // Optionally, only send a mock response for a specific endpoint
    // and method.
    //System.assertEquals('http://example.com/example/test', req.getEndpoint());
    //System.assertEquals('GET', req.getMethod());
    // Create a fake response
    HttpResponse res = new HttpResponse();
    res.setHeader('Content-Type', 'application/json');
    res.setBody('{"example":"test"}');
    res.setStatusCode(200);
    return res;
  }
}
2
  • Generally this error occur when you are making any DML operation in between any API callout. Also you can try to move that Test.setMock before Test.startTest()
    – RCS
    Commented May 23, 2018 at 11:39
  • @RCS already tried that. Commented May 23, 2018 at 11:45

3 Answers 3

3

At the very least, the error isn't very mysterious at all.

The first thing you're doing in your test class is creating a record and performing a DML insert. In the case where you execute the else bit of your saveUserInfo() method, you're additionally performing an update prior to the callout.

Callouts cannot happen after DML.

There are a couple of other issues with your code (looks like you copy/pasted the if/else handling the callout response to the block that handles when a Registration__c already exists, and are trying to insert a record that already exists).

I'll focus on fixing the "uncommited work pending" error(s). Perhaps the simplest way to fix this is to do a little bit of dependency injection. In a nutshell, when a method needs a certain piece of information, dependency injection is where you pass the required thing(s) into the class or method instead of creating them in the method itself.

A very simple example

public void myMethod(){
    // This data is required for the method to work, and we're creating it
    //   inside the method itself.
    Integer i = 2;
    system.debug(1 + i);
}

public void myMethodWithDependencyInjection(Integer i){
    // Passing the required data as a parameter can make the method more flexible
    //   and easier to test.
    system.debug(1 + i);
}

How does this help you?

Well, the entire reason why you require the DML insert before calling your saveUserInfo() method is because you query to see if there is at least one Registration__c record. If you remove that query, you remove the need for the DML insert in your test method.

public PageReference saveUserInfo(List<Registration__c> registrationList) {                   
    // Instead of querying and seeing if we get any results, just check to see
    //   if the list that was passed in is empty or not
    //if([Select First_Name__c, Last_Name__c, Company__c, Job_Title__c, Email__c, Phone__c, Address_Line_1__c, Address_Line_2__c, City__c, State__c, Country__c, Zip__c, Total_fund__c From Registration__c Limit 1].size() == 0)
    if(registrationList.isEmpty())
    {            
        Registration__c createRegInfo = new Registration__c(
            First_Name__c = RegData.First_Name__c, 
            Last_Name__c = RegData.Last_Name__c, 
            Email__c = RegData.Email__c,
            Phone__c = RegData.Phone__c, 
            Address_Line_1__c = RegData.Address_Line_1__c, 
            Address_Line_2__c = RegData.Address_Line_2__c,
            Company__c = RegData.Company__c, 
            Job_Title__c = RegData.Job_Title__c, 
            Zip__c = RegData.Zip__c, 
            City__c = RegData.City__c,
            State__c = RegData.State__c, 
            Country__c = RegData.Country__c
        );

        // callout code

        // response handling

    } else {
        // Having the list as a parameter allows us to remove this query as well.
        // We can simply pull the record from the list
        //Registration__c createRegInfo = [Select First_Name__c, Last_Name__c, Company__c, Job_Title__c, Email__c, Phone__c, Address_Line_1__c,
                                         Address_Line_2__c, City__c, State__c, Country__c, Zip__c, Total_fund__c FROM Registration__c LIMIT 1 FOR UPDATE];

        createRegInfo = registrationList[0];
        createRegInfo.First_Name__c = RegData.First_Name__c;
        createRegInfo.Last_Name__c = RegData.Last_Name__c;
        createRegInfo.Email__c = RegData.Email__c;
        createRegInfo.Phone__c = RegData.Phone__c;
        createRegInfo.Address_Line_1__c = RegData.Address_Line_1__c
        createRegInfo.Address_Line_2__c = RegData.Address_Line_2__c;
        createRegInfo.Company__c = RegData.Company__c;
        createRegInfo.Job_Title__c = RegData.Job_Title__c;
        createRegInfo.Zip__c = RegData.Zip__c;
        createRegInfo.City__c = RegData.City__c;
        createRegInfo.State__c = RegData.State__c;
        createRegInfo.Country__c = RegData.Country__c;

        // This is the other issue.
        // Since you don't re-query this record, and would update it anyway after
        //   the callout, I'm not sure you need this
        //update createRegInfo;                      

        // callout code

        // response handling
    }
}

With that, you can fix most of the issues you've run into.

Your test method would change to

@isTest static void existingUserCheck()  {                
    Registration__c checkInsert = new Registration__c(
        First_Name__c = 'Nilkamal', 
        Last_Name__c = 'Gotarne', 
        Company__c = 'Xoyal It Services',
        Job_Title__c = 'Salesforce Developer', 
        Email__c = '[email protected]', 
        Phone__c = '+917020111572',
        Address_Line_1__c = 'Bund garden, pune 411015', 
        Address_Line_2__c = '', 
        City__c = 'Pune',
        State__c = 'Maharashtra', 
        Country__c = 'India', 
        Zip__c = '411001'
    );

    Register CheckRegister = new Register(new ApexPages.StandardController(new Registration__c()));
    CheckRegister.RegData.First_Name__c = 'Nilkamal';
    CheckRegister.RegData.Last_Name__c = 'Gotarne';
    CheckRegister.RegData.Email__c = '[email protected]';
    CheckRegister.RegData.Phone__c = '9021567897';
    CheckRegister.RegData.Address_Line_1__c = 'Earth';
    CheckRegister.RegData.Address_Line_2__c = 'Mars' ;
    CheckRegister.RegData.Company__c = 'Xoya';
    CheckRegister.RegData.Job_Title__c = 'Sales Man';
    CheckRegister.RegData.Zip__c = '411015';
    CheckRegister.RegData.City__c = 'Poona';
    CheckRegister.RegData.State__c = 'Maha Rashtra';
    CheckRegister.RegData.Country__c = 'Bharat';
    Test.startTest();        
    Test.setMock(HttpCalloutMock.class, new MockHttpResponseRegisterInfo());
    // Construct a new list, using the curly-bracket syntax to initialize the list
    //   with the record you've already set up
    CheckRegister.saveUserInfo(new List<Registration__c>{checkInsert});
    Test.stopTest();        
}

There are still issues

This won't solve all of the problems in your code (you're effectively duplicating your callout and response handling code, your current approach is likely to break if there is more than a single Registration__c record).

This will also introduce a few new problems like:

  • In the case where the Registration__c record is supposed to exist, my test record won't have an Id, so the update DML will not work
  • You presumably want to call this method from a command button, which means you can't pass an argument directly to the method

Both are valid concerns. My answer is getting pretty long, so I won't go into much detail.

The first could be fixed by using an if(Test.isRunningTest()) block, or by rearranging your code.

Instead of

  • Prepare record
  • Callout
  • Handle response

You could (since your callout doesn't use any data from the Registration__c record)

  • Callout
  • Handle non-200 response code
  • Prepare record in the block that handles the 200 response

The second issue could be solved by having your command button call a method which in turn calls your saveUserInfo() method.

1
3

Your class makes a callout after you perform DML.

From Salesforce knowledge base:

You can't make callouts, HTTP or otherwise, once you have made changes to the database. It's true that you cannot make callouts with pending transactions in the request context. Also, it's not possible to do an explicit commit. So your only way out is to make the callout in a separate context.

Your class performs an HTTP callout to Twilio after performing an update DML. This is not allowed. You need to either reorder these pieces of functionality or break the callout into a separate transaction using an @future method or Queueable Apex

5
  • plz explain how can i reorder these pieces of functionality or break the callout into a separate transaction using an @future or Queueable Apex method. I am new to salesforce. Commented May 23, 2018 at 11:49
  • I'm not sure how to explain it more than than. What clarification are you looking for?
    – David Reed
    Commented May 23, 2018 at 11:52
  • future is not allowed on global class or method and i cannot reorder because it is in the exact sequence of execution. how can i use queable apex method ? Commented May 23, 2018 at 11:59
  • 1
    Your class isn't global. I've added a link to the documentation on Queueable Apex to my answer. There is also a good Trailhead on asynchronous Apex that I recommend if you aren't familiar with this area.
    – David Reed
    Commented May 23, 2018 at 12:08
  • ok thank u ill check and if it solves my problem ill get back to you Commented May 23, 2018 at 12:10
0
@isTest
public static void methodNamesShouldBeCamelCase(){
   //Test.startTest() was originally here and the test failed 'Uncommited work pending'...

   /*DML Statements*/

   delete sObj;

   Test.startTest(); //Moving this after DML caused the test to Pass

   /* Callouts */

   Test.stopTest();
}

When I encountered this error upon a unit test failing, I was able to move the Test.startTest() call after all DML operations (I was performing a delete) and the test was successful.

You must log in to answer this question.

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