Amr.K94
Amr.K94

Reputation: 11

Salesforce: An APEX test class for a web service callout

I've been trying to figure out the test class for a web service callout. I have the four different classes. API_Member which has all the parameters with all the information and matching of the fields. I have a API_Response class and I've got an AccountTrigger and AccountTriggerHandler.

In the AccountTriggerHandler, the whole HTTPRequest and callout is handled where I call the API_Member class as well and then triggers it from the AccountTrigger.

Now the difficulty is the test class since I'm not an experienced developer.

How would I manage to handle the test class for this code (I've made changes in case it's some important information)?

public with sharing class AccountTriggerHandler {

    public void isAfterInsert(list<Account> accountNew) {
        for(Account a: accountNew) {
            sendData(a.Id);
        }
    }

    public void isAfterUpdate(list<Account> accountNew) {

    }

    @future(callout=true)
    public static void sendData(string accountId) {
        API_Member api = new API_Member(accountId);
        api.setupData();
        API_Member.Data data = api.dataList;
        string json = JSON.serialize(data); 
        system.debug('JSON' + json);
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        string url = 'http://api.XXXXXXXXXXXXXXXXXXXXXXX';
        string username = 'Ext-XXXXXXXXXXXXXXX-XXXXXXXXXXXXXX';
        string password = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
        Blob headerValue = Blob.valueOf(username + ':' + password);
        String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
        request.setHeader('Authorization', authorizationHeader); 
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        request.setEndpoint(url);
        request.setMethod('POST');
        request.setBody(json);
        HttpResponse response = http.send(request);
        if (response.getStatusCode() == 201 || response.getStatusCode() == 200 ) {
            API_Response responseCTRL = (API_Response) System.JSON.deserialize(response.getBody(), API_Response.class);
            list<API_Response.Meta> m = responseCTRL.Meta;
            System.debug('recordID='+m[0].recordID);
            api.setResponse(m[0].recordID,response.getStatus());
        }
    }
}

Here is the API_Member Class

public class API_Member {
    public Data dataList;
    public Account input;
    public Contact c;
    public class Data {
        public list<Info> data;
    }
    public class Info {
            public String active;
            public String address_city;
            public String address_country;
            public String address_line_1;
            public String address_line_2;
            public String address_state;
            public String address_zipcode;
            public String company_name;
            public String date_end;
            public String date_paid;
            public String date_start;
            public String email;
            public String membership_number;
            public String mobile;
            public String org_number;
            public String person_first_name;
            public String person_last_name;
            public String person_number;
            public String phone;
            public String status;
            public String web_address;
    }

    public API_Member(string accountId) {
        this.input = getAccount(accountId);
        this.c = getContact();
    }

    public void setupData(){
        dataList = new Data();
        Info data = new Info();
        data.active = 'true'; //To be verified
        data.address_city = input.Ort__c;
        data.address_country = '';
        data.address_line_1 = input.Adress1__c;
        data.address_line_2 = '';
        //data.address_state = input.L_n_Namn__c;
        data.address_zipcode = input.Postnummer__c;
        data.company_name = input.Name;
        data.date_end = input.Expiry_Date__c != null ? string.valueOf(input.Expiry_Date__c) : '';
        data.date_paid = input.Last_Renew__c != null ? string.valueOf(input.Last_Renew__c) : '';
        data.date_start = input.Last_Renew__c != null ? string.valueOf(input.Last_Renew__c): '';
        data.email = input.Medlems_Epost__c;
        data.membership_number = input.AccountNumber;
        data.mobile = input.Phone;
        data.org_number = input.Organisationsnummer__c;
        data.person_first_name = c != null ? c.FirstName : '';
        data.person_last_name = c != null ? c.LastName : '';
        data.person_number = c != null ? c.Personnummer__c : '';
        data.phone = input.Phone;
        data.status = input.Status_Pay__c;
        data.web_address = input.Website;

        dataList.data = new list<Info>{data};
    }

    public Contact getContact(){
        list<Contact> c = [ SELECT Id,
                            Personnummer__c,
                            LastName,
                            FirstName
                            FROM Contact
                            WHERE AccountId =: input.Id
                            ORDER BY CreatedDate
                            ASC
                            LIMIT 1
        ];
        if(c.size() > 0){
            return c[0];
        }
        return null;
    }

    public Account getAccount(string accountId){
        Account a = [SELECT Id,
                    Ort__c,
                    Adress1__c,
                    Postnummer__c,
                    Name,
                    Expiry_Date__c,
                    Last_Renew__c,
                    Medlems_Epost__c,
                    AccountNumber,
                    Phone,
                    Organisationsnummer__c,
                    Status_Pay__c,
                    F_RecordId__c,
                    F_Satus__c,
                    Website
                    FROM Account
                    WHERE Id =: accountId
                    LIMIT 1
        ];
        return a;
    }
    
    public void setResponse(string recordId, string Status) {
        input.F_RecordId__c  = recordId;
        input.F_Satus__c  = status;
        update input;
    }
}

Here is the API_Response Class

public class API_Response {

    public class Meta {
        public String recordID {get;set;} 
        public String href {get;set;} 
    }
    
    public List<Meta> meta {get;set;} 
    public List<Data> data {get;set;} 
    public Info info {get;set;} 
    public List<MetaField> metaField {get;set;} 
    
    public class MetaField {
        public String name {get;set;} 
        public Integer autoEntered {get;set;} 
        public Integer global_Z {get;set;} // in json: global
        public Integer maxRepeat {get;set;} 
        public String resultType {get;set;}         
    }
    
    public class Data {
        public String address_city {get;set;} 
        public String address_country {get;set;} 
        public String address_line_1 {get;set;} 
        public String address_line_2 {get;set;} 
        public String address_state {get;set;} 
        public String address_zipcode {get;set;} 
        public String company_name {get;set;} 
        public String date_end {get;set;} 
        public String date_start {get;set;} 
        public String email {get;set;} 
        public String membership_number {get;set;} 
        public String mobile {get;set;} 
        public String org_number {get;set;} 
        public String person_first_name {get;set;} 
        public String person_last_name {get;set;} 
        public String person_number {get;set;} 
        public String phone {get;set;} 
        public String status {get;set;} 
        public String upload_data_status {get;set;} 
        public String web_address {get;set;} 

    }
    
    public class Info {
        public String X_RESTfm_Version {get;set;} // in json: X-RESTfm-Version
        public String X_RESTfm_Protocol {get;set;} // in json: X-RESTfm-Protocol
        public Integer X_RESTfm_Status {get;set;} // in json: X-RESTfm-Status
        public String X_RESTfm_Reason {get;set;} // in json: X-RESTfm-Reason
        public String X_RESTfm_Method {get;set;} // in json: X-RESTfm-Method
        public String X_RESTfm_PHP_memory_limit {get;set;} // in json: X-RESTfm-PHP-memory_limit
        public String X_RESTfm_PHP_post_max_size {get;set;} // in json: X-RESTfm-PHP-post_max_size

    }
    
}

Upvotes: 1

Views: 7903

Answers (1)

eyescream
eyescream

Reputation: 19612

For the unit test you will have to mock (fake) the service you're calling. We can't call real services from unit tests because:

  • a deployment success/failure shouldn't depend on whether some 3rd party server is down
  • you don't want to send "my awesome test data!!!111" message to a production server without having a way to roll it back.

You need to write a piece of code that will pretend it is that service. It's up to you how awesome it'll be. It can always return same message. Or it can inspect the data it received and throw error if required field is missing. Really - you decide, you know best how many scenarios you should test (how do you handle errors returned?)

Check https://trailhead.salesforce.com/en/content/learn/modules/apex_integration_services/apex_integration_soap_callouts

The whole concept of mocking can be used for "normal" apex too, not just for callouts. Might be bit advanced topic though: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_stub_api.htm

Edit:

You'd need 2 classes you can mark as @isTest (so they don't count towards your normal max of code). This is REST API, right? Not SOAP? Maybe https://trailhead.salesforce.com/content/learn/modules/apex_integration_services/apex_integration_rest_callouts would be better. In a pinch you could make it as 1 class but it's bit messy, keep them separate?

First make the API_ResponseMock. It doesn't have to be global, public should be enough. Something like

@isTest
public class API_ResponseMock implements HttpCalloutMock {
    public HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('if you have sample JSON document that the service would return - paste it here. You must have something you used as the JSON2Apex input?');
        response.setStatusCode(200);
        return response;
    }

This is base, you probably don't want it to return same content on every "callout" but good enough.

And AccountTriggerHandlerTest would be "normal" unit test, with creation of test account. Something like

@isTest
static void testCallout() {
    // Set mock callout class 
    Test.setMock(HttpCalloutMock.class, new API_ResponseMock());
    Test.startTest();
    insert new Account(Name = 'unit test'); // put whatever required fields you have
    Test.stopTest(); // @future's don't run until this line.

    // hm, what now? You have anything to "assert"? Will this callout be updating account maybe? Setting a Task with status=Completed, anything you can check?
}

Upvotes: 1

Related Questions