1

After attending DF14 I have a fairly solid understanding of the HttpCalloutMock() when unit testing for REST callouts, but I'm having trouble implementing unit testing for the first time. The documentation I've gone through so far hasn't helped me connect all the dots for my use-case.

[REVISED QUESTION]

I have a trigger that fires after update of a Lead that checks if an Email has been added, and if so then it will execute Pardot.createProspectsByContact(Trigger.new); which makes two @future calls see here:

LeadTriggerHandler.cls

private void isEmailAdded (){
    for(Lead l : (List<Lead>) Trigger.new){
        Lead old = (Lead) Trigger.oldMap.get(l.Id);
        /* If Email was empty */
        if ( old.Email == null && l.Email != null ){
            /* Add Lead Prospect */
            Pardot.createProspectsByLead(Trigger.new);  <-- HERE IS WHEN THE ACTION STARTS
            System.debug('::LEAD SENT TO PARDOT::');
        }
    }
}

Pardot.cls

public static void createProspectsByLead (Lead[] leads){
    for (Lead l :leads) {
        APIRequest(
            'https://pi.pardot.com/api/prospect/version/3/do/create', // API Request
            'email='+l.Email // Required parameter
        );
        System.debug('::PARDOT EMAIL::'+l.Email);
    }
}

Then I have 2 separate test class files, LeadTest.cls & PardotTest.cls, both of these attempt to follow the practices listed here

1) LeadTest.cls (all my current test pass except this one)

static testMethod void addEmail(){
    Lead lead = createLead_basic();
    lead.Email = 'jsmith@smithandco.com';

    Test.startTest(); <-- NOT SURE IF THIS IS NECESSARY...
        update lead; <-- BECAUSE EMAIL WAS BLANK THIS WILL INVOKE Pardot.createProspectsByLead()
    Test.stopTest();
}

Which then leads me to the test class I'm having issues/questions with. Here is an outline of the methods involed

2) PardotTest.cls (I'm understanding that there should by a method for each method within Pardot.cls to account for complete coverage but I don't understand how to do that here)

/* Just as in Pardot.cls, but how do I make the UPDATE LEAD trigger use this function during testing
@isTest public static void createProspectsByLeadTEST (Lead[] leads){
    for (Lead l :leads) {
        APIRequestTEST(
            'https://pi.pardot.com/api/prospect/version/3/do/create',
            'email='+l.Email
        );
    }
}   

[ORIGINAL CONTEXT]

  • The methods that fetch the Global__c (Class.Pardot.getConnectorEmail) return empty since no data exist during the unit test.

    Questions 1: Can I create a separate unit test that populates this data to be used when this test is run? For example: -RESOLVED- (see @Keith C 's answer)

    Question 2: Do I need to put something in the Pardot.cls or PardotTest.cls that implements HttpCalloutMock? I don't understand when writing something like this, how it knows what method it is invoking at the time the test is executed (currently I am naming the test methods orignalMethodNameTest() ).

    Question 3: How do I use the response of the first HttpCalloutMock to pass into the next HttpCalloutMock, or do I even need to since it will be a mock call anyways? (Essentially I just need all the lines to hit, not that the data has to be associated like a real-world example, correct?)

Previous Questions

  • 1
    you are going to need MultiMockCallout if you have >1 httpcallout in a test transaction – cropredy Oct 25 '14 at 0:22
4

Below is the sort of test class I would write for this situation. I assume the aim is to test your own code not the 3rd party code so this aims to do the minimum to get the sequence of requests to work.

The core technique where multiple HTTP callouts are involved is to make your mock class support multiple requests by using if/else logic based on the incoming request.

@isTest
private class Test_Lead {

    private class Mock implements HttpCalloutMock {
        public HTTPResponse respond(HTTPRequest req) {
            HTTPResponse res = new HTTPResponse();
            res.setStatusCode(200);
            if (req.getEndpoint().endsWith('apikey') {
                // Return a fake API_Key in the response
                res.setBody(...);
            } else if (req.getEndpoint().endsWith('apirequest') {
                // Mock the response if necessary
                res.setBody(...);
            }
            return res;
        }
    }

    @isTest
    static void addEmail() {

        insertPardotConnectorEmail();

        Lead lead = createLead_basic();
        lead.Email = 'jsmith@smithandco.com';

        // HTTP calls get routed to mock instead of out to the internet
        Test.setMock(HttpCalloutMock.class, new Mock());

        Test.startTest();
        update lead;
        Test.stopTest();
    }

    private static Global__c insertPardotConnectorEmail(){
        //Create test data
        Global__c setting = new Global__c(
                Name = 'Pardot Connector Email',
                Value__c = 'pardot+admin@raprec.com'
        );
        insert setting;
        return setting;
    }

    private static Lead createLead_basic() {
        ...
    }
}

On your questions:

  1. Yes you can move some of the logic out to other classes but that is only worth doing if you have multiple test classes. Usually its simpler to have one test class with multiple test methods.
  2. Doesn't Pardot.cls already have its own test class? I'm assuming your aim is to test your own code and just mock enough environment so that Pardot.cls works and makes the necessary calls when you invoke it through your trigger.
  3. Again, I'm assuming that Pardot.cls will pass on the value and that you are not trying to verify that because the focus of the test is your own code.
  • Thanks, trying this now. #2) Yes I'm trying to test my own code, but I've never written a test before (new to SF) so I'm unsure how to do it and what the best practice is. My current thought is to have 3 test classes 1) test_lead.cls - generate lead data and invoke lead DML methods. 2) test_pardot.cls - when lead or contact is updated and Pardot.cls is triggered this class would create mock test. 3) test_global.cls - populate the fake credentials that are stored in the Global__c. Makes sense? – Xtremefaith Oct 27 '14 at 18:33
  • @Xtremefaith Using several classes seems unnecessary but if you have a reason to do that go ahead. – Keith C Oct 27 '14 at 20:37
  • my reasoning for that was for consistency and a structure that's reusable. For example, right now I have 1 trigger per Object, and 1 TriggerClassHandler for each Object, so naturally I was attempting to make 1 TestClass for each and keeping things like Pardot in their own test class so that any object can reuse those same methods and test scripts (just not sure how to make that happen. Could you restructure your response to say which snippets would go in those 3 files? or something along those line? – Xtremefaith Oct 27 '14 at 20:44
  • @Xtremefaith Just put the methods you want to re-use in ordinary public classes without isTest annotations, but keep your naming convention so its clear they are part of your tests. – Keith C Oct 27 '14 at 20:55
  • I revised my question to be more specific to my use case to clarify what I'm not understand thus far. Also I reconsidered my code segregation and went from 3 test classes to 2 so that should simply some things (removed Global_Test.cls). Thanks for your help so far. – Xtremefaith Oct 28 '14 at 0:33

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.