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
Baylor PeakBaylor Peak 

Help with Test Class for Batch Apex with HTTP callouts

Hello all,

I wrote a batch class that sends data to an endpoint but I am not good with test classes at all:(

Here is my code:

global class CircleMembershipBatchProcesor implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {
    
// Start Method
    global Integer recordsProcessed = 0;
    global Database.QueryLocator start(Database.BatchableContext bc) {
        String apexClassId = '01p1U00000QoPw6QAF';
        List<AsyncApexJob> jobs = [SELECT CreatedDate FROM AsyncApexJob WHERE JobType = 'BatchApex' AND ApexClassId =: apexClassId ORDER BY CreatedDate DESC LIMIT 1];
        DateTime jobDate = null;
        if (jobs != null && jobs.size() > 0) {
            System.debug('using AsyncApexJob');
            AsyncApexJob job = jobs[0];
            jobDate = job.CreatedDate;
        } else {
            System.debug('using System.now()');
            jobDate = System.now().addHours(-1);
        }
        String dateStr = jobDate.format('yyyy-MM-dd\'T\'HH:mm:ss.SSSZ');
        System.debug(dateStr);
        String Query='SELECT Id, FirstName, LastName, Email, npo02__MembershipJoinDate__c, npo02__MembershipEndDate__c, npo02__LastMembershipDate__c FROM Contact WHERE npo02__MembershipJoinDate__c != NULL AND (CreatedDate > ' + dateStr + ' OR LastModifiedDate > ' + dateStr + ')';
        return Database.getQueryLocator(Query);
    }
// Execute Method
    global void execute(Database.BatchableContext bc, List<Contact> scope){
        String JSONString = JSON.serialize(scope);
        HttpRequest request = new HttpRequest();
        HttpResponse response = new HttpResponse();
        Http http = new Http();

	// Retrieve username and password from Custom Setting
        CircleMembershipBatchSettings__c myCS1 = CircleMembershipBatchSettings__c.getValues('default');
        String username = myCS1.username__c;
        String password = myCS1.password__c;

    // Add basic authentication to header        
        // Create blob of user:pass
        Blob headerValue = Blob.valueOf(username + ':' + password);
        // Base 64 Encode the blob and prepend "Basic "
        String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
        // Add the basic auth string to the Request Header
        request.setHeader('Authorization', authorizationHeader);

        request.setHeader('Content-Type', 'application/json');
        request.setEndpoint('https://th-webhook-test.herokuapp.com/webhook');
        request.setMethod('POST');
        request.setBody(JSONString);
        response = http.send(request);
        if (response.getStatusCode() == 200) {
            String jsonResponse = response.getBody();
            System.debug('Response-' + jsonResponse);
            System.debug(JSONString);
        } else {
            throw new CircleMembershipBatchProcesorException('Callout was not successful');
        }
    }
// Finish Method
    global void finish(Database.BatchableContext bc){
        Id job= bc.getJobId();
        System.debug(job);
    }
}
ShirishaShirisha (Salesforce Developers) 
Hi Baylor,

Greetings!

Can you please check the sample code provided in the below document to cover Httprequest:

https://salesforce.stackexchange.com/questions/185163/a-testclass-for-restresource-httppost

Kindly let me know if it helps you and close your query by marking it as solved so that it can help others in the future.

Warm Regards,
Shirisha Pathuri
Baylor PeakBaylor Peak

For this part

//As Per Best Practice it is important to instantiate the Rest Context
    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();
    req.requestURI = '/services/apexrest/Something'; //Request URL
    req.httpMethod = 'POST';
Would I instead use?
req.requestURL = ('https://th-webhook-test.herokuapp.com/webhook');
Also, how do I also test the Batch Apex wihin the same Test class?


 

Agustin BAgustin B
Hi Baylor, you can mock your http callout response, check this link for that:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_testing_httpcalloutmock.htm

For testing you batch put the Database.executeBatch between Test.startTest() and Test.stopTest()

If this solves your issue please mark this answer as correct. It may help others.

 
Baylor PeakBaylor Peak

OK, here is what I have so far fro my Test Class, but only getting 36% code coverage:

@isTest
private class CircleMembershipBatchProcesorTest {
        @isTest static void testPostCallout() {
        // Set mock callout class
        Test.setMock(HttpCalloutMock.class, new CircleMembershipHttpCalloutMock());
        // This causes a fake response to be sent
        // from the class that implements HttpCalloutMock.
            HttpResponse response = MakePostCallout.doPostCallout();
        // Verify that the response received contains fake values
        String contentType = response.getHeader('Content-Type');
        System.assert(contentType == 'application/json');
        String actualValue = response.getBody();
        System.debug(response.getBody());
        String expectedValue = '{"name":"this is a test"}';
        System.assertEquals(actualValue, expectedValue);
        System.assertEquals(200, response.getStatusCode());
    }
    //Test Apex Batch Code
    @testSetup static void test_data() {
    Contact con = new Contact(
        LastName = 'Test'
    );

    INSERT con;
    }
    @isTest static void testApexBatch() {
        Test.startTest();
        Database.executeBatch(new CircleMembershipBatchProcesor(), 100);
        Test.stopTest();
        System.assertEquals(10, [select count() from contact where Lastname = 'Test']);
    }
    
        
}
May I get some assistance in finishing this class, what am I missing, etc? I'm getting there, but not yet;)

Regards,
Baylor
Agustin BAgustin B
Hi Baylor, so 36% means the batch is not entering the execute part? In that case verify that your query in line 20 does retrieve any records.
If you are getting inside the execute method please show me which lines are covered and which one are not.

I ll wait for your reply
Baylor PeakBaylor Peak

Here is what is showing that is not covered:

Under Start Method:

global Integer recordsProcessed = 0;

String apexClassId = '01p1U00000QoPw6QAF';
        List<AsyncApexJob> jobs = [SELECT CreatedDate FROM AsyncApexJob WHERE JobType = 'BatchApex' AND ApexClassId =: apexClassId ORDER BY CreatedDate DESC LIMIT 1];
        DateTime jobDate = null;
        if (jobs != null && jobs.size() > 0) {

AsyncApexJob job = jobs[0];
            jobDate = job.CreatedDate;

         jobDate = System.now().addHours(-1);

      String dateStr = jobDate.format('yyyy-MM-dd\'T\'HH:mm:ss.SSSZ');

String Query='SELECT Id, FirstName, LastName, Email, npo02__MembershipJoinDate__c, npo02__MembershipEndDate__c, npo02__LastMembershipDate__c FROM Contact WHERE npo02__MembershipJoinDate__c != NULL AND (CreatedDate > ' + dateStr + ' OR LastModifiedDate > ' + dateStr + ')';
        return Database.getQueryLocator(Query);ystem.debug(dateStr);

Under Execute Method:

String JSONString = JSON.serialize(scope);
        HttpRequest request = new HttpRequest();
        HttpResponse response = new HttpResponse();
        Http http = new Http();


        String username = myCS1.username__c;
        String password = myCS1.password__c;

        request.setEndpoint('https://th-webhook-test.herokuapp.com/webhook');
        request.setMethod('POST');
        request.setBody(JSONString);
        response = http.send(request);
        if (response.getStatusCode() == 200) {
            String jsonResponse = response.getBody();
            System.debug('Response-' + jsonResponse);

Under Finsh Method:

System.debug(job);
Agustin BAgustin B
merge the two test methods into one. So you can have the executebatch in the same class as the mock
Baylor PeakBaylor Peak

Oops, here are the line that are covered taht you asked for:

Start:

User-added image
Execute:

User-added image

Finish:
User-added image

And thank you for helping me with this:)

Baylor PeakBaylor Peak
OK, I merged the two methods and now my code coverage is none:(