1

I'm trying to create a test class for a trigger that executes business logic based on the a custom Geolocation field on the quote (this calls a class that does and http callout to retrieve the street address via reverse-geolocation).

However, the Geolocation codes are always being read as null, even when they are populated in the test class.

Here is the test class:

@isTest
private class quoteTriggerTest {

@TestSetup static void createOpportunity(){
    Opportunity opp = new Opportunity();
    opp.Name = 'Test Opportunity';
    opp.CloseDate = date.today().addDays(30);
    opp.StageName = 'Qualification';
    insert opp;
}

@isTest static void newQuoteWithGeolocation(){
    Quote q = new Quote ();
    q.OpportunityId = [SELECT Id FROM Opportunity WHERE Name='Test Opportunity' LIMIT 1].Id;
    q.Name = 'Test Quote with Geolocation';
    q.Job_Site_Geolocation__latitude__s = 36.072528;
    q.Job_Site_Geolocation__longitude__s = -95.925575;
    insert q;
    System.debug('Quote Name: ' + q.Name);
    System.debug('Quote Geolocation: ' + q.Job_Site_Geolocation__c);
    System.debug('Job Site Address: ' + q.Job_Site_Street__c + ', ' + 
       q.Job_Site_City__c + ', ' + q.Job_Site_State_Province__c + ' ' + 
       q.Job_Site_Postal_Zip__c + ', ' + q.Job_Site_Country__c);
}

@isTest static void newQuoteWithoutGeolocation(){
    Quote q = new Quote ();
    q.OpportunityId = [SELECT Id FROM Opportunity WHERE Name='Test 
        Opportunity' LIMIT 1].Id;
    q.Name = 'Test Quote without Geolocation';
    insert q;
    System.debug('Quote Name: ' + q.Name);
    System.debug('Quote Geolocation: ' + q.Job_Site_Geolocation__c);
    System.debug('Job Site Address: ' + q.Job_Site_Street__c + ', ' + 
       q.Job_Site_City__c + ', ' + q.Job_Site_State_Province__c + ' ' + 
       q.Job_Site_Postal_Zip__c + ', ' + q.Job_Site_Country__c);
    q.Name = 'Updated Test Quote without Geolocation';
    update q;
    System.debug('Quote Name: ' + q.Name);
    System.debug('Quote Geolocation: ' + q.Job_Site_Geolocation__c);
    System.debug('Job Site Address: ' + q.Job_Site_Street__c + ', ' + 
       q.Job_Site_City__c + ', ' + q.Job_Site_State_Province__c + ' ' + 
       q.Job_Site_Postal_Zip__c + ', ' + q.Job_Site_Country__c);
}

@isTest static void updatedQuoteWithGeolocation(){
    Quote q = new Quote ();
    q.OpportunityId = [SELECT Id FROM Opportunity WHERE Name='Test 
       Opportunity' LIMIT 1].Id;
    q.Name = 'Test Quote Updated with Geolocation';
    insert q;
    q.Job_Site_Geolocation__latitude__s = 36.072528;
    q.Job_Site_Geolocation__longitude__s = -95.925575;
    update q;
    System.debug('Quote Name: ' + q.Name);
    System.debug('Quote Geolocation: ' + q.Job_Site_Geolocation__c);
    System.debug('Job Site Address: ' + q.Job_Site_Street__c + ', ' + 
      q.Job_Site_City__c + ', ' + q.Job_Site_State_Province__c + ' ' + 
      q.Job_Site_Postal_Zip__c + ', ' + q.Job_Site_Country__c);
}

}

And here is the trigger:

trigger quoteTrigger on Quote (after insert, after update) {

List<Quote> quoteList = new List<Quote>();

if(Trigger.isInsert){
    for(Quote q : Trigger.new){
        if (q.Job_Site_Geolocation__c != NULL) {
            quoteList.add(q);
        }
    }
}

if(Trigger.isUpdate){
    for(Quote q : Trigger.new){
        Quote oldQuote = Trigger.oldMap.get(q.Id);
        Quote newQuote = Trigger.newMap.get(q.Id);
        if(oldQuote.Job_Site_Geolocation__c != 
           newQuote.Job_Site_Geolocation__c){
            quoteList.add(q);
        }

    }
}
for(Quote q : quoteList){
    System.debug('Quote Id: ');
}

retrieveAddress.getAddress(quoteList);

}

I can see from the debug log that the variables in the test class do have the geolocation codes, but when the the DML gets executed and the trigger fires, the geolocation field is null.

When I manually create a Quote with the geolocation data in the database, the trigger also fails to see that the geolocation field is not null.

Is there something additional I need to do in the trigger so that it can see the geolocation field?

7
  • Here is the full debug log from running the test class: github.com/mlregal/debug-logs/blob/master/… Jul 5, 2019 at 15:13
  • Can you add code for retrieveAddress.getAddress and are you using Future or queuable? Jul 5, 2019 at 16:06
  • @pranay-jaiswal The class isn't actually being called because the quoteList variable is empty. Jul 5, 2019 at 16:08
  • You mean in your test method updatedQuoteWithGeolocation after DML update , Job_Site_Geolocation__c is still blank? Jul 5, 2019 at 16:11
  • 1
    You have to query it from the database after doing update. , q = [SELECT Id , Job_Site_Geolocation__c FROM Quote WHERE Id=:q.id]; System.debug(q.Job_Site_Geolocation__c ; Jul 5, 2019 at 16:18

2 Answers 2

2

In addition to Phil's points, when you do DML that causes automation to run, and then you want to test that the automation worked (or in production code, get the results of that automation), you need to re-query the record(s). Until you do, the object only has what you've set on it (except the record Id, which does get added to the in-memory object when you insert it). That's the main reason why your System.debug() statements show NULL for those fields. And, you can't do much with the compound Geolocation field, including use it in SOQL. You have to work with the lat/long components. So, your newQuoteWithGeolocation() test will end up looking something like this:

@isTest static void newQuoteWithGeolocation(){
    Quote q = new Quote ();
    q.OpportunityId = [SELECT Id FROM Opportunity WHERE Name='Test Opportunity' LIMIT 1].Id;
    q.Name = 'Test Quote with Geolocation';
    q.Job_Site_Geolocation__latitude__s = 36.072528;
    q.Job_Site_Geolocation__longitude__s = -95.925575;

    // RetrieveAddressMock() could be the name of your class that implements the
    // HttpCalloutMock interface
    // https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_testing_httpcalloutmock.htm
    Test.setMock(HttpCalloutMock.class, new RetrieveAddressMock());

    Test.startTest();
        insert q;
    Test.stopTest();

    q = [SELECT Id, Name, Job_Site_Geolocation__latitude__s, Job_Site_Geolocation__longitude__s, 
                Job_Site_Street__c, Job_Site_City__c, Job_Site_State_Province__c,
                Job_Site_Postal_Zip__c, Job_Site_Country__c
                FROM Quote
                WHERE Name = 'Test Quote with Geolocation'
                LIMIT 1]

    System.debug('Quote Name: ' + q.Name);
    System.debug('Quote Geolocation: ' + q.Job_Site_Geolocation__latitude__s + ',' + q.Job_Site_Geolocation__longitude__s);
    System.debug('Job Site Address: ' + q.Job_Site_Street__c + ', ' + 
       q.Job_Site_City__c + ', ' + q.Job_Site_State_Province__c + ' ' + 
       q.Job_Site_Postal_Zip__c + ', ' + q.Job_Site_Country__c);

    // Debugs are fine while you're writing your code, but you'll want some asserts 
    System.assertEquals('[Street address returned by mock class]',q.Job_Site_Street__c,'Street address does not match mock');
    System.assertEquals(36.072528, + q.Job_Site_Geolocation__latitude__s,'Latitude got changed.');

}
2
  • 1
    This pointed me in the right direction - it looks like the Trigger can't actually read the geolocation fields either. When I updated the Trigger to look at Job_Site_Geolocation__latitude__s and Job_Site_Geolocation__longitude__s, the test ran correctly. Jul 5, 2019 at 16:42
  • 1
    Yep, I was about to update my answer with this - did some tests, and directly debugging the compound Geo field always gave NULL. Jul 5, 2019 at 16:45
2

You need to create a callout mock and use Test.startTest/Test.stopTest around the code that invokes the callout. See the documentation for details.

Fundamentally, tests cannot use real callouts. Using a mock, your tests can check that the code calling the callout behaves correctly for the details it gets back. This lets you simulate "happy path" and failure cases in your tests.

5
  • (I may have misinterpreted your question, reading it again, but consider these points)
    – Phil W
    Jul 5, 2019 at 15:39
  • Yes, I have that set up, but right now, I'm stuck at getting the trigger to recognize that the records I've inserted/updated have actual values in the geolocation fields so they get added to the quoteList variable. Jul 5, 2019 at 16:03
  • Yeah, I wasn't sure as I couldn't see anything in your test class.
    – Phil W
    Jul 5, 2019 at 16:04
  • Do you have some process builder, workflow or lightning flow, or a separate trigger, that could be clearing these values?
    – Phil W
    Jul 5, 2019 at 16:09
  • No. This is a brand new org and this is the only automation other than the CPQ managed package, which doesn't touch these custom fields. Jul 5, 2019 at 16:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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