function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Scott BradyScott Brady 

Inbound Email Handler - Test Class Issues

Hi All,

I have the following, basic, inbound email handler class:
 
global class ProcessInboundEmailSupport implements Messaging.InboundEmailHandler {

  global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,
    Messaging.InboundEnvelope envelope) {

    Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();

     BMCServiceDesk__Incident__c ticket = new BMCServiceDesk__Incident__c();   
        if([select count() from Account where Account.Support_Email_Service__c = :email.toAddresses] == 0) {

            }
            else {
                 ticket.BMCServiceDesk__FKAccount__c = [select Id from Account where Account.Support_Email_Service__c = :email.toAddresses].Id;
                 ticket.BMCServiceDesk__shortDescription__c = email.subject;
                 ticket.BMCServiceDesk__incidentDescription__c = email.plaintextbody;
                 ticket.BMCServiceDesk__FKContact__c = [select Id from Contact where contact.Email = :email.fromAddress].Id;
                 ticket.Source__c = 'Email';
                 ticket.BMCServiceDesk__FKCategory__c = 'a2kS0000000lg1Q';
                 ticket.Emailed_From__c = email.fromAddress;
                 }
          
    insert ticket;

    System.debug('====> Created ticket '+ticket.Id);
    
            
    BMCServiceDesk__IncidentHistory__c action = new BMCServiceDesk__IncidentHistory__c();
        action.BMCServiceDesk__FKAction__c = 'a0QS0000003wjRR';
        action.BMCServiceDesk__note__c = 'From:' +' '+ email.fromAddress +'\n'+ 'To:' +' '+ email.toAddresses +'\n'+ 'Cc:' +' '+ email.ccAddresses +'\n'+ 'Subject:' +' '+ email.subject +'\n'+ 'Body:' +' '+ email.plaintextbody;
        action.BMCServiceDesk__FKIncident__c = ticket.id;
            insert action;

    if (email.binaryAttachments != null && email.binaryAttachments.size() > 0) {
      for (integer i = 0 ; i < email.binaryAttachments.size() ; i++) {
        Attachment attachment = new Attachment();
        
        attachment.ParentId = ticket.Id;
        attachment.Name = email.binaryAttachments[i].filename;
        attachment.Body = email.binaryAttachments[i].body;
        insert attachment;
      }
    }

    return result;

  }

}

The class works just fine for me, however, when I am trying to create a test class, I keep receiving nullpoint exceptions - it's always line 24
 
@isTest
public class TestSupportHandler {
 public static testMethod void TestMyController() {

  // create a new email and envelope object
  Messaging.InboundEmail email = new Messaging.InboundEmail() ;
  Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();

  // setup the data for the email
  email.subject = 'Test Email Subject';
  env.fromAddress = 'sbrady@digitalhands.com';

  // add an attachment
  Messaging.InboundEmail.BinaryAttachment attachment = new Messaging.InboundEmail.BinaryAttachment();
  attachment.body = blob.valueOf('my attachment text');
  attachment.fileName = 'textfile.txt';
  attachment.mimeTypeSubType = 'text/plain';

  email.binaryAttachments =
    new Messaging.inboundEmail.BinaryAttachment[] { attachment };

  // call the email service class and test it with the data in the testMethod
  ProcessInboundEmailSupport emailProcess = new ProcessInboundEmailSupport();
  emailProcess.handleInboundEmail(email, env);

  // query for the ticket the email service created
  BMCServiceDesk__Incident__c ticket = [select id, Emailed_From__c  from BMCServiceDesk__Incident__c
    where Emailed_From__c = :env.fromAddress];

  System.assertEquals(ticket.Emailed_From__c,'sbrady@digitalhands.com');
 
  // find the attachment
  Attachment a = [select name from attachment where parentId = :ticket.id];

  System.assertEquals(a.name,'textfile.txt');

}
}

I have two questions:
1. Why am I receiving that error when I believe that variable is defined above on the previous line.
2. I am not the best with test classes, so I know that this will not test ALL of my class and I will need to add to it - however would this test class point me in the right direction? Or do I need to change quite a bit?

Any assistance that could be provided would be wonderful, I am still learning a bit about the test classes/requirements as I go.   
Best Answer chosen by Scott Brady
Alain CabonAlain Cabon
"In that case, would i be able to remove the seealldata tag?" :
yes, remove always the tag @isTest (SeeAllData=true) as soon as you can. Your test should be ok without this tag given that you have removed the hardcoded ids. This tag is mandatory in few cases but not here.

"I'm now at 72% code coverage and just need to add my other steps from the original class (category etc)."

create an account with its required fields and at least Support_Email_Service__c = email.toAddresses 

A good practice is to use a test setup method but it is not mandatory.

Account acc = new Account(name='xxx', Support_Email_Service__c =  '<email to addresses value >', ... );
insert acc;

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_testsetup_using.htm

Regards

All Answers

Alain CabonAlain Cabon
Hi,

You have used hardcoded IDs. You will need to use @isTest (SeeAllData=true)

ticket.BMCServiceDesk__FKCategory__c = 'a2kS0000000lg1Q';  // without any account this one is not used.

action.BMCServiceDesk__FKAction__c = 'a0QS0000003wjRR';
insert action;  // should fail because the BMCServiceDesk__FKAction__c doesn't exist.

Regards
Scott BradyScott Brady
Hi Alain,

I'm still confused.  I understand the change for SeeAllData, but i'm still getting 

System.NullPointerException: Attempt to de-reference a null object
External entry point Class.TestSupportHandler.TestMyController: line 24, column 1

Is that pointing to the email itself? That's the part that is tripping me up.  I know how to test to make sure the handler did the correct assignments to the resulting ticket that will be created from the email, but I can't even get the test class to run because of the error above.  I do understand that if the account is not found, the handler should fail - are you telling me to add the category/insert action code to the test class as well?

Is the null object being referenced the ticket that it's trying to create when the email is sent through the test?
Alain CabonAlain Cabon
Hi,

We still need to investigate this.

Your code is very close to the sample of a testing class of Salesforce:
https://help.salesforce.com/articleView?id=code_inbound_email.htm&type=0
// call the email service class and test it with the data in the testMethod 
ProcessInboundEmailSupport emailProcess = new ProcessInboundEmailSupport(); 
system.debug('emailProcess: [' + emailProcess + ']' );
emailProcess.handleInboundEmail(email, env);

Launching the test with the Developer Console (open your testing class and click on the button "Run Test" (top, left)) and looking at the Logs  (tab, bottom left) for the result of the system.debug added above.

User-added image

Regards.
Scott BradyScott Brady
Attaching my logs:User-added image
Alain CabonAlain Cabon
Hi,

The problem occurs first at the line [9] : System.NullPointerException: Attempt to de-reference a null object

so the problem is here: if([select count() from Account where Account.Support_Email_Service__c = :email.toAddresses] == 0) {

email.toAddresses is a list of String.

Just add the line below in bold:

// setup the data for the email
email.subject = 'Test Email Subject';
email.toAddresses = new List<String>{'titi@test.com'};
env.fromAddress = 'sbrady@digitalhands.com';

There is still the problem of the hardcoded Ids (even with @isTest (SeeAllData=true).

Your test could work in the sandbox org now but it could fail in production. You should try to get these Ids with two requests SOQL using the name or external Ids (keys as string) of the targetted objects. 

Regards

Alain
Scott BradyScott Brady
:( wow - didn't even realize that of course i need a to address in the email for testing.  I'm now at 72% code coverage and just need to add my other steps from the original class (category etc).

I have also updated the harcoded ID's with queries for the name (which is the same in prod as in sandbox).  Thanks for the tips.

In that case, would i be able to remove the seealldata tag?
Scott BradyScott Brady
another thing to add - i don't think that the test class is actually creating the ticket from the email that is sent in.  When i use my query and system assert, it's actually picking an existing ticket in  my sandbox and systemassertequals against that record. I believe all we have done is just trick the test class into completing, it's not actually a great test class (that's my fault).
 
Alain CabonAlain Cabon
"In that case, would i be able to remove the seealldata tag?" :
yes, remove always the tag @isTest (SeeAllData=true) as soon as you can. Your test should be ok without this tag given that you have removed the hardcoded ids. This tag is mandatory in few cases but not here.

"I'm now at 72% code coverage and just need to add my other steps from the original class (category etc)."

create an account with its required fields and at least Support_Email_Service__c = email.toAddresses 

A good practice is to use a test setup method but it is not mandatory.

Account acc = new Account(name='xxx', Support_Email_Service__c =  '<email to addresses value >', ... );
insert acc;

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_testsetup_using.htm

Regards
This was selected as the best answer
Scott BradyScott Brady
Well done on this one, Alain - you helped me a ton and also helped me learn a bit more.  Thanks again for your help.

Answered.