4

Working on my first call out class. When a contact is created or has an email changed we need to make a call out to a third party service that verifies the email address.

Everything is working excep the final assertion. I expect a result of valid but the result is null. I have been reading through the documentation but not making sense of the rules around DML and mocks. I have included my call out class, test class and Mock. How can I get my assertion to pass and validate that the field I want updated by my callout has been updated?

Web-service

public with sharing class NeverBounceCallout {

@Future(callout=true)
public static void checkNeverBounce(Id id){
    Contact  contact = [SELECT Email, Never_Bounce_Result__c FROM Contact WHERE Id =: id LIMIT 1];
    String email = contact.Email;
    String url = 'https://api.neverbounce.com/v4/single/check?key={api_key}&email=' + email;

    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(url);
    request.setMethod('GET');
    HttpResponse response = http.send(request);
    if(response.getStatusCode()==200){
        Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());

            contact.Never_Bounce_Result__c = (String)results.get('result');
            System.debug('json body = ' + results);
            update contact;
        }
    }
}

My Mock

@IsTest
global class NeverBounceMock implements HttpCalloutMock{

global HttpResponse respond(HttpRequest request){
    System.assertEquals('GET', request.getMethod());

    HttpResponse response = new HttpResponse();
    response.setHeader('Content-Type', 'application/json');
    response.setStatusCode(200);
    response.setBody('{"status" : "success", "result" : "valid"}');
    return response;

}

Test

@IsTest
private class NeverBounceTest {
@IsTest
static void NeverbounceEmailTest() {
    Account a = new Account(Name = 'Test acc');
    insert a;
    Contact c = new Contact(LastName = 'Test Con', AccountId = a.Id, Email='Test@test.com');
    insert c;

    Test.startTest();
    Test.setMock(HttpCalloutMock.class, new NeverBounceMock());
    NeverBounceCallout.checkNeverBounce(c.Id);
    Contact contactToCheck = [SELECT Never_Bounce_Result__c FROM Contact WHERE Id =: c.Id LIMIT 1];
    //System.assertEquals('valid', contactToCheck.Never_Bounce_Result__c);
    Test.stopTest();

}
}
  • 1
    Do you intend to set this -- contact.Never_Bounce_Result__c = (String)results.get('result'); instead? The reason being your mock response returns an attribute named result instead of results. – Jayant Das Apr 22 at 17:03
2

The problem here is because of the way you are testing a future callout.

Because the updates are being carried out in a future method, thus while asserting, you don't get the value while you are asserting it between Test.startTest() and Test.stopTest(). Refer to this trailhead on how to test future methods, except below (emphasis mine).

The system collects all asynchronous calls made after the startTest. When stopTest is executed, all these collected asynchronous processes are then run synchronously. You can then assert that the asynchronous call operated properly.

So you will need to make the assertions in your test class, after Test.stopTest().

Test.startTest();
Test.setMock(HttpCalloutMock.class, new NeverBounceMock());    
NeverBounceCallout.checkNeverBounce(c.Id); 
Test.stopTest();

// assertion now
Contact contactToCheck = [SELECT Never_Bounce_Result__c FROM Contact WHERE Id =: c.Id LIMIT 1];
System.assertEquals('valid', contactToCheck.Never_Bounce_Result__c);

You will still need the below details to make sure your response returns correct value.


Your mock returns:

response.setBody('{"status" : "success", "result" : "valid"}');

and that you are trying to set the value of an attribute named results (notice the extra s here):

contact.Never_Bounce_Result__c = (String)results.get('results');

And thus your field Never_Bounce_Result__c would have never been set with the expected value here, thus failing your assertion.

For your assertion to work, you will need to set result as expected in the response and that it should be written as:

contact.Never_Bounce_Result__c = (String)results.get('result'); // no ending s here

Or, if you expect results, then that attribute needs to be set in the response accordingly.

  • Hi Jayant and thank you. I did make that change you suggested. But forgot to edit my post. The assertion still fails. I will update now. – Brooks Johnson Apr 22 at 17:20
  • Have you put a debug to see what do you receive when you query it, no assertion, just a debug. – Jayant Das Apr 22 at 17:21
  • 1
    @BrooksJohnson I have updated my answer based on what I found while trying to replicate this issue. – Jayant Das Apr 22 at 18:14
  • 1
    @JayantDas Was JUST about to write a new answer with that information! – Thomas Taylor Apr 22 at 18:29
  • 1
    @ThomasTaylor I completely missed the future annotation at the first place and had in fact thought all wrong as how it was behaving here. – Jayant Das Apr 22 at 18:30
5

Notice how you set the value in this field:

contact.Never_Bounce_Result__c = (String)results.get('results');

Does your mock ever set a results attribute? No. You need to add this attribute to your JSON map ('{"status" : "success", ...}').

  • Hi Adrian, I made that change and verified it in the debug log but the assertion is still failing. I updated the code above. And thank you for your help. – Brooks Johnson Apr 22 at 16:49
  • @BrooksJohnson Check your singular/plural. It's result in the mock's response and results in the class. BTW - you'd have been better off debugging the Contact field in the class, since that's what your assert is testing. – Thomas Taylor Apr 22 at 17:03
  • Yeah you need to use {..., "results": "value"}. – Adrian Larson Apr 22 at 18:18

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.