+ Start a Discussion
Kev BrierKev Brier 

Test class Invocable Method calling @future class to send email with VF attachment

Hi,

Hoping someone can help to point us in the right direction, it seems that whatever we do, we are unable to get better than 26% code coverage with our Invocable Email Class calling an @future Method to send a single email with a visualforce attachement generated on the record. The Class works like a charm and does exactly what it is meant to do and we are even able to dummy an email being sent and assert the fact it was but it doesn't get close to covering the code. 

Any guidance would be greatfully appreciate, as even though we run at 95% coverage, i'm not prepared to push a class with only 26% even though it works fully.

Please help save my sanity? 
 
Invoked from process builder with 100% coverage

public class ProcessHandler {
    public class NPS{
        @InvocableVariable(required=true)
        public Id npsid;
        @InvocableVariable(required=true)
        public String mname;
        @InvocableVariable(required=true)
        public String oname;
        @InvocableVariable(required=true)
        public String subject;
        @InvocableVariable(required=true)
        public String client;
        @InvocableVariable(required=true)
        public String md;
        @InvocableVariable(required=true)
        public String body;
    }
    
    @InvocableMethod
   public static void invokeapexcallout(NPS[] nps) {
     Futureapexcallout.apexcallout(nps[0].mname, nps[0].oname, nps[0].npsid, nps[0].subject, nps[0].client,nps[0].md,nps[0].body);

    }
}
 
@Future Method with only 26% coverage

public class Futureapexcallout{

  @future(Callout=true)
  public static void apexcallout(string mname,string oname,Id NPSId, String subject, String client, string md, string body){
         
        
         
        pageReference pdfPage = Page.NPS_Survey;
        pdfPage.getParameters().put('id',NPSID);
        
        if(Test.isRunningTest()){
        blob b = blob.valueOf('Unit.Test');
        }else{
        blob b = pdfpage.getContentaspdf(); 

        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');
        efa1.setBody(b);

        String addresses;
        email.setSubject(subject);
        email.setToAddresses(new List<String> {mname});
        email.setCCAddresses(new List<String> {oname});
        email.setPlainTextBody(body);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
    }
}
}
 
Test Class, probably really badly written :)

@isTest
private class testProcessHandler
{
private static testMethod void testProcessHandler() {
    
    test.startTest();
    
        User u2 = new User(Alias = 'nUr32', Email='newuser@testorg32.com', 
        EmailEncodingKey='UTF-8', LastName='Testing12', LanguageLocaleKey='en_US', 
        LocaleSidKey='en_US', ProfileId = '00e90000001yvFD' , 
        TimeZoneSidKey='America/Los_Angeles', UserName='newuser32@testorg.com',External_Use__c='SRK1132AA',Operating_Country__c='Global',Region__c='Global' ,Team__c='Client Services - Global');
        insert u2;
     
        Account a = new Account(Name = 'Test A',
        BillingCountry = 'UK',
        Ownership_Type__c = 'Private',
        Phone = '07826533392',
        Industry = 'Property development & management',
        Type = 'Customer Direct');
        insert a;
    
        Contact c = new Contact (FirstName='Test', LastName='Test', AccountId = a.Id,
        Phone = '07826533392');
        insert c;    
        
        Net_Promoter__c n = new Net_Promoter__c (Account__c=a.Id,
        Assigned_To__c=u2.id,
        Client_Visiting__c=c.id,
        Activity_Date__c = datetime.newInstance(2017, 9, 15, 12, 30, 0),
        End_Time__c=datetime.newInstance(2017, 9, 15, 14, 30, 0),
        Site_Location__c='Test'                                                                                   
        );
        insert n;
        
        n.Survey_Status__c='Complete';
        n.Score_Updated__c=TRUE;
        update n;
        
        List<ProcessHandler.NPS> Params = new List<ProcessHandler.NPS>();

        ProcessHandler.NPS p = new ProcessHandler.NPS();
        p.npsid = n.id;
        p.mname = n.MD_Name__c;
        p.oname = n.Ops_Director__c;
        p.subject = n.subject__c;
        p.client = n.client_visiting__r.FirstName;      
        p.md = n.MD_Email__c;
        p.body = n.Body__c; 
        
        Params.add(p);
        
        ProcessHandler.Invokeapexcallout(params);
        
        Futureapexcallout.apexcallout(n.MD_Name__c, n.Ops_Director__c, n.id, n.subject__c,n.client_visiting__r.FirstName, n.MD_Name__c, N.Body__c );
        Integer invocations = Limits.getEmailInvocations();
        
        pageReference pdfPage = Page.NPS_Survey;
        pdfPage.getParameters().put('id',n.id);
        
        if(Test.isRunningTest()){
        blob b = blob.valueOf('Unit.Test');
        }else{
        blob b = pdfpage.getContentaspdf(); 
         
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');

        String addresses;
        email.setSubject(n.subject__c);
        email.setToAddresses(new List<String> {n.MD_Name__c});
        email.setCCAddresses(new List<String> {n.Ops_Director__c});
        email.setPlainTextBody(N.Body__c);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
        
        test.stopTest();
        
        system.assertEquals(p.npsid, n.id);       
        system.assertEquals(1, invocations, '1 email should be sent');
}           
}
}

 
Best Answer chosen by Kev Brier
Alex SelwynAlex Selwyn
1) In Futureapexcallout class, make sure the blob b; is declared outside.
blob b;

if(Test.isRunningTest()){
        b = blob.valueOf('Unit.Test');
        }else{
        b = pdfpage.getContentaspdf(); 
    }

Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');
        efa1.setBody(b);

        String addresses;
        email.setSubject(subject);
        email.setToAddresses(new List<String> {mname});
        email.setCCAddresses(new List<String> {oname});
        email.setPlainTextBody(body);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

2) In testProcessHandler class, remove the following lines. ProcessHandler.Invokeapexcallout(params); should call your apexcallout method. This need not be called again.
 
Futureapexcallout.apexcallout(n.MD_Name__c, n.Ops_Director__c, n.id, n.subject__c,n.client_visiting__r.FirstName, n.MD_Name__c, N.Body__c );
        Integer invocations = Limits.getEmailInvocations();
        
        pageReference pdfPage = Page.NPS_Survey;
        pdfPage.getParameters().put('id',n.id);
        
        if(Test.isRunningTest()){
        blob b = blob.valueOf('Unit.Test');
        }else{
        blob b = pdfpage.getContentaspdf(); 
         
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');

        String addresses;
        email.setSubject(n.subject__c);
        email.setToAddresses(new List<String> {n.MD_Name__c});
        email.setCCAddresses(new List<String> {n.Ops_Director__c});
        email.setPlainTextBody(N.Body__c);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});


 

All Answers

Alex SelwynAlex Selwyn
if(Test.isRunningTest()){
        blob b = blob.valueOf('Unit.Test');
        }else{
        blob b = pdfpage.getContentaspdf(); 
    }

Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');
        efa1.setBody(b);

        String addresses;
        email.setSubject(subject);
        email.setToAddresses(new List<String> {mname});
        email.setCCAddresses(new List<String> {oname});
        email.setPlainTextBody(body);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

Move the email logic out of the else condition in 'Futureapexcallout'. See if this works.

Thanks,
Alex.
Kev BrierKev Brier
Thanks Alex for looping back. We can't seem to get the correct logic here, we've managed to move the SingleEmailMessage call above, which actually increases the coverage to 35%. However, any other changes seem to either give a variable issue or the test fails for required fields missing on the email attachment body. 

I've seen some stuff about not using getcontentaspdf in test classes since winter 16 but again we're struggling to get the test to pass without this. 

Are you able to share how this should be annotated? 
Alex SelwynAlex Selwyn
Can you pls try this? the variable blob should be outside of the if condition
 
blob b;

if(Test.isRunningTest()){
        b = blob.valueOf('Unit.Test');
        }else{
        b = pdfpage.getContentaspdf(); 
    }

Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');
        efa1.setBody(b);

        String addresses;
        email.setSubject(subject);
        email.setToAddresses(new List<String> {mname});
        email.setCCAddresses(new List<String> {oname});
        email.setPlainTextBody(body);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

 
Kev BrierKev Brier
Thanks Alex for sticking with me on this one. Now we just keep getting the same error, required field missing email Body.

Looks like all the variables on the test class are all null, even though the parameters have been set above. 

I can't see how the test should fail with this subtle change.  
Alex SelwynAlex Selwyn
1) In Futureapexcallout class, make sure the blob b; is declared outside.
blob b;

if(Test.isRunningTest()){
        b = blob.valueOf('Unit.Test');
        }else{
        b = pdfpage.getContentaspdf(); 
    }

Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');
        efa1.setBody(b);

        String addresses;
        email.setSubject(subject);
        email.setToAddresses(new List<String> {mname});
        email.setCCAddresses(new List<String> {oname});
        email.setPlainTextBody(body);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

2) In testProcessHandler class, remove the following lines. ProcessHandler.Invokeapexcallout(params); should call your apexcallout method. This need not be called again.
 
Futureapexcallout.apexcallout(n.MD_Name__c, n.Ops_Director__c, n.id, n.subject__c,n.client_visiting__r.FirstName, n.MD_Name__c, N.Body__c );
        Integer invocations = Limits.getEmailInvocations();
        
        pageReference pdfPage = Page.NPS_Survey;
        pdfPage.getParameters().put('id',n.id);
        
        if(Test.isRunningTest()){
        blob b = blob.valueOf('Unit.Test');
        }else{
        blob b = pdfpage.getContentaspdf(); 
         
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

        Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
        efa1.setFileName('NPS.pdf');

        String addresses;
        email.setSubject(n.subject__c);
        email.setToAddresses(new List<String> {n.MD_Name__c});
        email.setCCAddresses(new List<String> {n.Ops_Director__c});
        email.setPlainTextBody(N.Body__c);
        email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
        Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});


 
This was selected as the best answer
Kev BrierKev Brier
Thanks again Alex, the only word the comes to mind now is Baffled!

Making the relevant changes doesn't enable the test class to pass, we still keep getting the same errors as we did before, email body missing etc.

However, the code coverage on the future callout now shows 94% but continues to fail. I don't think i've ever seen this before, how can it fail but show such high coverage. Its not like we lose the coverage on the process handler, this still continues to show 100%.

Clearly we're very close and something really minimal is wrong but it's not obvious to me.  
Kev BrierKev Brier
Thanks Alex, i'm going to mark the above as best answer on the basis we've improve the code coverage to 94% but the test class seems to continue to fail.

I've seen lots of stuff about not using GetContentAsPDF in a test class and therefore I'm sure this is probably the issue. Whilst we still seem to run into the problem the variables are null whatever we do and I'm not getting the same errors as others do when using getcontent. 

If you do have any other thoughts, please go ahead and share them. Hopefully helping me and others that land on this post.