Salesforce Asked by Austin Evans on October 4, 2021
I have a Queueable class MyQueueableClass
which in turns calls an outbound web service class.
public void execute(QueueableContext qc){
try{
GlobalCallout.makeCallout(caseId);
} catch (Exception ex){
System.debug('Exception: ' +ex);}
}
And in class: GlobalCallout
we are making a call out to an external service and performing other stuff. While writing a test class for the above Queueable Class
, my call out is getting failed, as I am familiar with: We cannot perform an actual call out in test class. So, I picked a different route: Creating mock data for the class: GlobalCallout
and right before the mock call out, I have System.enqueueJob(new MyQueueableClass(caseSC.Id));
assuming it would get the mock data from the context.
When I ran the test class, I get System.HttpResponse[Status=null, StatusCode=0]
. Can someone tell me if I am doing it wrong? If my approach is not valid, can I get some insights on making it right?
My implementation for test class:
@isTest
public class MyQueueableTestClass{
static HttpResponse response;
Static String successData= 'Success';
Static String failureData= 'Faied';
@testSetup static void testData() {
//Create a test case record
}
public class MockSuccess implements HttpcalloutMock {
public HTTPResponse respond(HTTPRequest req) {
response = new HttpResponse();
response.setbody(successData);
response.setstatuscode(200);
return response;
}
}
static testmethod void testForSuccess(){
Case caseSC = [Select Id From Case Limit 1];
Test.setMock(HttpCalloutMock.class, new MockSuccess());
Test.startTest();
System.enqueueJob(new MyQueueableClass(caseSC.Id));
System.debug('Response::'+response);
Test.stopTest();
}
}
I am getting the debug log as:
Response::null
Also, the debug for response within the web service call out class:
System.HttpResponse[Status=null, StatusCode=200]
I am sure, I might be missing something very small.
I'm not sure if something wrong in your code/org (didn't find any) or its core issue in SF related to Queueable & setMock.
The simple solution will be to use Test.isRunningTest() before your http.send. You can either construct dummy response there or you can use your mock class to get the test response. Probably something like:
HttpReponse res = Test.isRunningtest() ? new MockSuccess().response(req) : http.send(req);
Answered by Liron C on October 4, 2021
similar to this reference below can you try keeping your Test.setMock after Test.StartTest
Test.startTest();
Test.setMock(HttpCalloutMock.class, mock_obj);
// your method call
Test.stopTest();
FYR.. https://www.thephani.com/test-classes-for-future-methods-callout-true/
Answered by Rajesh Banglore on October 4, 2021
You need to define mock request & response to test all Callout related scenarios (200, 404, 406, 500 etc)
Implement HttpCalloutMock
to determine what kind of response your code should get
@isTest
public class MyHttpMockClass implements HttpCalloutMock{
// map<url, response>
public Map<String, HttpResponse> responseMap;
// map<url, true/false>, you only need to set true for urls where you want to test exception cases
public Map<String, Boolean> sendExceptionMap;
//add more functions/variables as per your requirement
public HttpResponse respond(HttpRequest request){
if(request.getEndPoint() == 'the endpoint i wanna test'){
// use responseMap for cleaner code
HttpResponse response = new HttpResponse();
//create response body here or in a helper method
response.setHeader('Content-Type', 'application/json');
response.setBody('{"field":"value"}');
response.setStatus('success');
response.setStatusCode(200);
return response;
// you can create class level map to store responses for 200, 404, etc
// and use that map & another variable to determine when to send what response
}
}
}
Set all these values before Test.startTest()
@isTest
public static void test_fn(){
// some logic, or data creation
MyHttpMockClass mobk_obj = new MyHttpMockClass();
// set responses
Test.startTest();
Test.setMock(HttpCalloutMock.class, mock_obj);
//you method call
Test.stopTest();
//Assert Results
}
Hope this helps.
Since a queueable class doesnt return values, you can't directly get HttpResponse by executing Queueable class. In a real-life scenario, a queueable class will make a callout, get response and update/create/delete a record. You can create response in such a way that the data is affected in an expected way. Then after Test.stopTest, you can assert that indeed target record(s) have been modified/created/deleted.
This way you can test callout that happens in a Queueable class
Answered by mritzi on October 4, 2021
A little addition to the recommendations of the previous responders:
Test.stopTest()
since until it is called your enqueued job will not finish. In other words, the job hasn't finished yet at the line of your debug call.I've faced the queueables that perform DMLs after the callout finishes, and about those ones, I can say that they worked correctly.
Answered by Jack Veromeev on October 4, 2021
Following points (specific to apex test class execution) might give you a better insight into what is going on here. Static variables in an apex test class are reset between:
Salesforce does this in order to maintain truly independent testing for each method or transaction boundary. I couldn't find out salesforce documentation on point #2 specific to the scenario you have mentioned, but this complies to the way async code is executed in apex. Also, I have tested out this scenario in the past, which led me to this understanding.
In your code, the Test.startTest()
method starts an additional execution context (with a fresh set of limits) and apex test execution starts keeping a tab on all the async method calls. When Test.stopTest()
is executed, it triggers all the async method calls or jobs to be executed synchronously. In this case, since it calls a queueable job (which I'm assuming in turn calls a future method), the code executes in its own transaction boundary. So, completion of this code execution would reset the static variable to the original context. I believe that the internal mechanism on how exactly this happens is something Salesforce should answer.
Now, in your code, the debug statement to check the response
value is declared right before the Test.stopTest()
. This implies that the test execution hasn't yet called the callout method i.e., the mock http respond
method hasn't been called. So, the response value is null as declared in the original context. Even if you move this debug statement below the Test.stopTest()
, you won't be able to check it's value because the test execution would have reset it to the original value.
Inside the web service call out class, you get the following response value because (in your mock http respond
method) you are not setting the status
value but only the statusCode
.
System.HttpResponse[Status=null, StatusCode=200]
So, your mock http callout class and its methods are working fine, but the usage of static variables is not the right approach in this specific test assertion or verification (explicitly because the callout happens thru an queueable apex). If you were to test the same callout synchronously called via a separate class (just for testing purpose as I don't recommend it for production scenario), you will find that the static response variable retains the values, since the test execution stays within the same transaction boundary.
As others have pointed out,
Test.setMock
method and the HttpResponse
is correctly populated in the mock http callout implementation, the call out response would always be successful.Hope this helps.
Answered by arut on October 4, 2021
It seems like you are implementing you Mock inside of the Test class itself. I would create it as another class instead, as @Aks is suggesting although it does not need to be global, but public. Also note that testMethod keyword is deprecated, you should use this instead:
@IsTest static void testForSuccess(){}
Additionally from my point of view (I do not know which logic does your main class involve) you should not check response status, but the logic that your class follows whether the statusCode and body are the expected or not.
Answered by Gabriel Serrano Salas on October 4, 2021
before running the test you need to call the Test.setMock()
Do something like this
First create a httpmock class
global class YOURMOCKCLASS implements HttpCalloutMock
{
global HttpResponse response(HttpRequest req)
{
HttpResponse res = new HttpResponse();
res.setHeader({});
res.setBody({});
res.setStatusCode(200);
return res;
}
}
Then call that class from your test class.
Test.setMock(HttpCalloutMock.class, new YOURMOCKCLASS());
Test.startTest();
...
...
Test.stopTest();
Answered by Aks on October 4, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP