function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Michael Hedrick 2Michael Hedrick 2 

Test class issues with call out

Hello All,
I have an issue trying to figure out the Test classes for this code.  I know I need a test class for this controller and a Mock Call Out class but I have been gettign continous errors in my test classes.  Any help would be appreciated.

Regards,
M.


public with sharing class JdeOrderController {

    Account account;
    List<JdeOrderHeader> jdeData;
    string message;
        
    public JdeOrderController () {
        message = '';
        account = [SELECT stuff FROM Account WHERE Id = :ApexPages.currentPage().getParameters().get('id')];
        if(String.isEmpty(account.stuff) || !account.JDE__c.isNumeric()) {
            message = 'BLA BLA BLA' ;
        }
        else {
            jdeData = RetrieveJdeData();
        }
    }
    
    public string getMessage() {
        return message;
    }
    
    public List<JdeOrderHeader> getJdeData() {
        return jdeData;
    }

    private List<JdeOrderHeader> RetrieveJdeData() {
        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http http = new Http();

        JdeIntegration jde = new JdeIntegration();
        String url = jde.buildUrl(account, 'order');
        
        if (url == '') {
            return null;
        }

        req.setEndpoint(url);
        req.setMethod('GET');
        res = http.send(req);
    
        if(res.getstatusCode() == 200 && res.getbody() != null) {    
            List<JdeOrderHeader> jdeDataList = (List<JdeOrderHeader>)json.deserialize(res.getbody(), List<JdeOrderHeader>.class);
            return jdeDataList;
        }

        return null;
    }
}
Best Answer chosen by Michael Hedrick 2
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael as per this link (https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_controller_error_handling.htm) try to call all static methods(declare your methods in controller as static)  and set corresponding Visual force Pages with parameters, (https://developer.salesforce.com/forums/?id=906F00000008ye4IAA) i am providing you an over all idea of how to achieve this here is the pseudo code...
@isTest
public class testjdeorderController {
	@isTest
    public static void testController(){
        
        //prepare test data in such a way u check both the logic in constructor i.e insert account with stuff field        is empty or JDE__C is not numberic
        Account aIf=new account();
        aIf.stuff='';
        aIf.JDE__C='sdsd';
        insert aIf;
       
        // query this account inserted and save in setsomeVariable
       // you need to refer to current page and add corresponding data
        System.Test.setCurrentPageReference(new PageReference('Page.yourPage')); 
		System.currentPageReference().getParameters().put('id', setsomeVariable);
        
        JdeOrderController jdeCon=new JdeOrderController();
        
        
        System.assertEquals(jdeCon.getMessage(),'BLA BLA BLA');
        
        
        
    }
    @isTest 
    public static void testController1(){
        //prepare test data in such a way u check second logic
        Account aIf=new account();
        aIf.stuff='sdsd';
        aIf.JDE__C=1;
        insert aIf;
        //first you need to refer to current page and add corresponding data
        // query this account inserted and save in setsomeVariable
      
        System.Test.setCurrentPageReference(new PageReference('Page.yourPage')); 
		System.currentPageReference().getParameters().put('id', setsomeVariable);
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
        
        //this constuctor enters in to else block as per the data and make a call out
        JdeOrderController jdeCon=new JdeOrderController();
        
        
        System.assertEquals(jdeCon.jdeData,'{"foo":"bar"}');
        
        
    }
    
 


Mock Call Out
@isTest
global class MockHttpResponseGenerator 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('', req.getEndpoint());
        System.assertEquals('GET', req.getMethod());
        
        // Create a fake response
        HttpResponse res = new HttpResponse();
        
        res.setBody('{"foo":"bar"}');
       
        return res;
    }
}

This is, how i approach to this solution, if you come to know how to solve it better do post the test code that you tried implementing sot that we can move forward!

Reference to HttpMockCallOut  (https://developer.salesforce.com/blogs/developer-relations/2013/03/testing-apex-callouts-using-httpcalloutmock.html)
 

All Answers

Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael as per this link (https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_controller_error_handling.htm) try to call all static methods(declare your methods in controller as static)  and set corresponding Visual force Pages with parameters, (https://developer.salesforce.com/forums/?id=906F00000008ye4IAA) i am providing you an over all idea of how to achieve this here is the pseudo code...
@isTest
public class testjdeorderController {
	@isTest
    public static void testController(){
        
        //prepare test data in such a way u check both the logic in constructor i.e insert account with stuff field        is empty or JDE__C is not numberic
        Account aIf=new account();
        aIf.stuff='';
        aIf.JDE__C='sdsd';
        insert aIf;
       
        // query this account inserted and save in setsomeVariable
       // you need to refer to current page and add corresponding data
        System.Test.setCurrentPageReference(new PageReference('Page.yourPage')); 
		System.currentPageReference().getParameters().put('id', setsomeVariable);
        
        JdeOrderController jdeCon=new JdeOrderController();
        
        
        System.assertEquals(jdeCon.getMessage(),'BLA BLA BLA');
        
        
        
    }
    @isTest 
    public static void testController1(){
        //prepare test data in such a way u check second logic
        Account aIf=new account();
        aIf.stuff='sdsd';
        aIf.JDE__C=1;
        insert aIf;
        //first you need to refer to current page and add corresponding data
        // query this account inserted and save in setsomeVariable
      
        System.Test.setCurrentPageReference(new PageReference('Page.yourPage')); 
		System.currentPageReference().getParameters().put('id', setsomeVariable);
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
        
        //this constuctor enters in to else block as per the data and make a call out
        JdeOrderController jdeCon=new JdeOrderController();
        
        
        System.assertEquals(jdeCon.jdeData,'{"foo":"bar"}');
        
        
    }
    
 


Mock Call Out
@isTest
global class MockHttpResponseGenerator 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('', req.getEndpoint());
        System.assertEquals('GET', req.getMethod());
        
        // Create a fake response
        HttpResponse res = new HttpResponse();
        
        res.setBody('{"foo":"bar"}');
       
        return res;
    }
}

This is, how i approach to this solution, if you come to know how to solve it better do post the test code that you tried implementing sot that we can move forward!

Reference to HttpMockCallOut  (https://developer.salesforce.com/blogs/developer-relations/2013/03/testing-apex-callouts-using-httpcalloutmock.html)
 
This was selected as the best answer
Michael Hedrick 2Michael Hedrick 2
Thank you for your thorough response. I have a simple Test class similiar to what you supplied and since I could not even get it to save I posted the Controller without my test code. It turned out to be a typo:
The Test Class code saved successfully but in the Developer console it fails.
"Methods defined as TestMethod do not support Web service callouts"
 
@isTest
public class Test_JDEIntegration {
    @isTest static void TestJDECOnnection() {

      Account testAccount = new Account(Name = 'Test Name', JDE__C = '11111');
      insert testAccount ;
  
       Account myTestId = [SELECT ID From Account where JDE__C = '11111'];
       PageReference myVfPage = Page.JDEWebServiceTest2 ;
       system.test.setCurrentPage(myVfPage);

       ApexPages.currentPage().getParameters().put('Id',myTestId.Id);  //Pass id to VF page
       ApexPAges.StandardController sc = new ApexPages.StandardController(myTestId);
       JdeOrderController JDEtestclass = new JdeOrderController();
    }

}

In the second test Class I  had to remove this line of code "System.assertEquals(jdeCon.jdeData,'{"foo":"bar"}');" because it gave an error upon save
But I am assuming I need the Test_JDEIntegration2 class since this Test Class makes the Mock call? Do I need both 
 
@isTest
public class Test_JDEIntegration2 {
    @isTest static void TestJDECOnnection() {

      Account testAccount = new Account(Name = 'Test Name', JDE__C = '35317');
      insert testAccount ;   
     
       Account myTestId = [SELECT Id From Account where JDE__C = '35317'];
       PageReference myVfPage = Page.JDEWebServiceTest2 ;
       system.test.setCurrentPage(myVfPage);

       ApexPages.currentPage().getParameters().put('Id',myTestId.Id);//Pass id to VF page
       ApexPAges.StandardController sc = new ApexPages.StandardController(myTestId);
       Test.setMock(HttpCalloutMock.class, new MockHttpResponseGeneratorJDE());
        
        //this constuctor enters in to else block as per the data and make a call out
        JdeOrderController jdeCon=new JdeOrderController(); 
        
     //   System.assertEquals(jdeCon.jdeData,'{"foo":"bar"}');   Gave error "Variable is not visible: jdeData"
          }    
  }

But if I try running it in the Developer Console it gives the following error:
System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

Stack Trace:
Class.JdeOrderController.RetrieveJdeData: line 40, column 1
Class.JdeOrderController.<init>: line 14, column 1
Class.Test_JDEIntegration2.TestJDECOnnection: line 18, column 1

Any additional help will be appreiated
Regards,
M
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
HI Michael, for your second error try to follow this link (https://salesforce.stackexchange.com/questions/3486/testing-httpcallout-with-httpcalloutmock-and-unittest-created-data) as in the controller you were using soql query in constructor this is causing the issue !
Michael Hedrick 2Michael Hedrick 2
Hello Akhilesh,
I tried the first suggestion of using @isTest(SeeAllData=true) which I know is not considered best practive but I wanted to just tryy to get a successful test performed.
So this time when I run a Test I get the following errro:

System.AssertException: Assertion Failed: Expected: http://api.salesforce.com/foo/bar, Actual: https://mycutomurl/?param=f5q76QGBTbk1GJX7sHQgsknxOfqm7zMpOFmYdIefNI4%2FP05xJKENQBdnPyhQNyT%2FFeG3KvwN8gh%2F18h%2BNwSjNA%3D%3D

I think part of this issue is that I am encrypting the url in my Controller by calling another Class,
JdeIntegration jde = new JdeIntegration();
 String url = jde.buildUrl(account, 'order');
 
Or is this still related to the SQOL Query in my Controller?

Thanks,
M
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael I dont think so it is due to SOQL Quey this time, just try to comment out assertEquals and try to run the code and see if you can achieve atleast few lines of code Coverage. we can improve the rest based on that later.

Thank you!
Michael Hedrick 2Michael Hedrick 2
Akhilesh,
I commented out the following 2 lines of code in the Mock Call Out Class.
//   System.assertEquals('http://api.salesforce.com/foo/bar', req.getEndpoint());
//   System.assertEquals('GET', req.getMethod());

Now when I run the Test I get the following error:
System.JSONException: Malformed JSON: Expected '[' at the beginning of List/Set

Stack Trace:
Class.System.JSON.deserialize: line 15, column 1
Class.JdeOrderController.RetrieveJdeData: line 43, column 1
Class.JdeOrderController.<init>: line 14, column 1
Class.Test_JDEIntegration2.TestJDECOnnection: line 18, column 1

Something new evertime....
Thanks for your continued help Akhilesh.

Regards,
Michael
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael, to fix this issue i need to replicate all the dependent classes related to this class, with out exact code i cant help. if possible can you edit the question with all the classes related to this class and explain schema objects that this class was using!

thank you!
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael, try to follow this link (https://developer.salesforce.com/forums/?id=906F000000090abIAA)  use your JSON response in the string and see if it solved !
Michael Hedrick 2Michael Hedrick 2
Hello Akhilesh,
I do not think I can edit the original post so I will add the additional classes below.
The Controller is listed in the Original Post.
I would rather not post the code for the 'JdeIntegration' Class.  This is the Class that encrypts the URL to the remote server.
JDEWebServiceTest2  is the visualforce page.  I did not think you needed this code but let me know if you do and I will post it. 


These first 2 Classes are for defining the Data Structure
Global class JdeOrderHeader { 
       
    public string Company {get; set;}
    public integer OrderNumber {get; set;}
    public string OrderType {get; set;}
    public string SoldTo {get; set;}
    public string ShipTo {get; set;}
    public DateTime OrderDt {get; set;}
    public List<JdeOrderDetail> Details {get; set;}
    
    Global string FormattedOrderDt {
        get {
            if (OrderDt == null) {
                return '';
            }
            return OrderDt.format('MM/dd/yyyy');
           } set;  // add set to allow visibility in test class
    }
    
}
 
public class JdeOrderDetail {

    public string Company {get; set;}
    public integer OrderNumber {get; set;}
    public string OrderTpe {get; set;}
    public integer LineNumber {get; set;}
    public DateTime ShipDt {get; set;}
    public string ItemNumber {get; set;}
    public string NextStatus {get; set;}
    public integer QuantityOrdered {get; set;}
    public string UnitOfMeasure {get; set;}
    
    public string FormattedShipDt {
        get {
            if (ShipDt == null) {
                return '';
            }
            return ShipDt.format('MM/dd/yyyy');
        }
    }
}


Controller Test Class
@isTest(SeeAllData=true)
public class Test_JDEIntegration2 {
    @isTest static void TestJDECOnnection() {

    //  Account testAccount = new Account(Name = 'Test Name', JDE__C = '35317' );
    //  insert testAccount ;
          
     
        Account myTestId = [SELECT Id From Account where JDE__C = '35317'limit 1];
        PageReference myVfPage = Page.JDEWebServiceTest2 ;
        system.test.setCurrentPage(myVfPage);

        ApexPages.currentPage().getParameters().put('Id',myTestId.Id);//Pass id to page 
        ApexPAges.StandardController sc = new ApexPages.StandardController(myTestId);
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGeneratorJDE());
        
        //this constuctor enters in to else block as per the data and make a call out
        JdeOrderController jdeCon=new JdeOrderController();
        
        
      //  System.assertEquals(jdeCon.jdeData,'{"foo":"bar"}');
        
        
        }
        
  }

Mock Call Out
@isTest
global class MockHttpResponseGeneratorJDE implements HttpCalloutMock {
   // Implement this interface method
   
        global 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('{"foo":"bar"}');
        res.setStatusCode(200);
        return res;
    }
}

Pleas elet me know if you need me to Post any additional code.

Regards,
M
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Can you send me your sample json response!
Michael Hedrick 2Michael Hedrick 2
As in a screenshot wheere the data is displayed on the VF page?  Not 100% sure what you are asking. Sorry.
 
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael, try to follow this link (https://developer.salesforce.com/forums/?id=906F000000090abIAA)  use your JSON response in the string and see if it solved !
Michael Hedrick 2Michael Hedrick 2
Hello Akhilesh,
In the url you referenced I was looking at the last post on the page and was wondering do you know how they are defining the variable 'apxCls'?
Also, this edit is will be in teh Mock Call out Class?
Thanks,
M
 
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Hi Michael, what the link was trying to say is in the httpResonse of your call out, the way you get response from the endpoint is not an array
for example in the below code you can see that the response has normalizedAddresses which the deserialize method dont like so they suggested to remove that from the link using String replace methods (https://developer.salesforce.com/forums/?id=906F00000008v5QIAQ) after you get the response from the callout! 
JSONString = '{"normalizedAddresses":[{"confidence":0,"comprehension":0,"nameCount":2,"nameForm":"1","accoutntType":"NULL                                              NULL NULL NULL","normalizedLines":["ALLEN GORDON AND DEBBIE","NULL","NULL NULL NULL"],"names":[{"rootFirstName":"GORDON","firstName":"GORDON","middleName":"","lastName":"ALLEN","businessName":"","nameForm":"1"},{"rootFirstName":"DEBORAH","firstName":"DEBBIE","middleName":"","lastName":"ALLEN","businessName":"","nameForm":"1"}],"context":"a1mQ00000039BHlIAM","houseNumber":"","streetName":"","city":"","state":"","postalCode":""}]}';
They are defining the apxCls same as your jdeDataList.  this was in your controller!

 
Michael Hedrick 2Michael Hedrick 2
The endpoint is concatonated as followed:
myserverserver + '/?param=' + EncodingUtil.urlEncode(param, 'UTF-8');

param is made up of the Account.Id and JDE__c value.

Does that help any?  I will look again at your previous post.

Thanks,
M
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
In your controller, make this method static and make a call out in the anonymous block and observe the JSON response in the debug log!

private List<JdeOrderHeader> RetrieveJdeData() {
        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http http = new Http();

        JdeIntegration jde = new JdeIntegration();
        String url = jde.buildUrl(account, 'order');
        
        if (url == '') {
            return null;
        }

        req.setEndpoint(url);
        req.setMethod('GET');
        res = http.send(req);

// the response you get for this request is in JSON format, this format was not able to deserialize in the below code try to see this response
in the debug log and figur out!
System.debug('JSON Response of the call out'+res);

    
        if(res.getstatusCode() == 200 && res.getbody() != null) {    
            List<JdeOrderHeader> jdeDataList = (List<JdeOrderHeader>)json.deserialize(res.getbody(), List<JdeOrderHeader>.class);
            return jdeDataList;
        }

        return null;
    }
}
Michael Hedrick 2Michael Hedrick 2
Hello Akhilesh,,
I was finally able to successfully test my classes without errors.  That was a good but painful exercise.
So now the question is, how can I insert conditions into my test class to cover situations like where the JDE__c value is vlaid and when its Null.
Can that be done in a single Test Class.

Regards,
Akhilesh Reddy BaddigamAkhilesh Reddy Baddigam
Yeah Michael, it is possible to achieve this using test setup (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_testsetup_using.htm) instead of seeAllData= true and querying necessary data in positive and negative methods with in the same class. Make sure you make the call out in System.test.startTest() and System.test.stopTest() (https://salesforce.stackexchange.com/questions/3486/testing-httpcallout-with-httpcalloutmock-and-unittest-created-data)

If my solutions helped you to reach your goal, please choose this one as best answer. 

Thank you!