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
TheProjectSolutionTheProjectSolution 

How to Satisfy "test coverage" for a Trigger?

Hi all,

 

I'll start by saying I'm not a developer by trade, so please bear with me.

 

I've attempted to tweak some samples discussed on this forum to build a trigger that sends an email notification when an Attachment is added to a Contract record.

 

I think the code is right, but I don't understand how to write a proper "test class" and how to run it/verify that it's satisfied requirements to move to production.  Although the test class I copied runs successfully and causes the "code coverage" listing on the Apex Trigger in Sanbox to move to 93%, when I then try to move the trigger to production via a Change Set, I get this error:

 

"Test coverage of selected Apex Trigger is 0%, at least 1% test coverage is required" 

 

I'm guessing this is because my test class doesn't really test what it needs to (again, I don't really know what I'm doing here :)), but it's weird that I see that "93%" figure in sandbox when production says it's <1%.


If anyone can write a quick test class that WILL work to producitionalize this trigger, please advise!

 

Here's the trigger:

 

*******

 

trigger Send_Email on Attachment (after insert) {
List<Id> contractIds=new List<Id>();
for (Attachment att : trigger.new)
{
contractIds.add(att.parentId);
}
Map<Id, Contract> contracts=new Map<Id, Contract>();
contracts.putAll([select id, Name from Contract where id IN :contractIds]);
Attachment attach = trigger.new[0];
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {'blahblahblah@gmail.com'};
mail.setToAddresses(toAddresses);
mail.setReplyTo('donotreply@gmail.com');
mail.setSenderDisplayName('Salesforce Support');
Contract contract=contracts.get(attach.parentId);
if (null!=contract)
{
mail.setSubject('Attachment Added on : ' + contract.name + ' for ' + contract.Related_Opportunity__c);
}
mail.setHTMLBody('An attachment has been added to the above record. Please visit Salesforce for details.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}

*******

 

 

And here's the attempt at a test class:

 

*******

public class SendEmailTest {


static testMethod void verifyAttachmentDescriptionsWhereOverwritten()
{


// Perform our data preparation.

List<Attachment> attachments = new List<Attachment>{};

for(Integer i = 0; i < 200; i++){

    Attachment a = new Attachment(ParentID='800S0000000iyOp',Body =  Encodingutil.base64Decode('VGVzdA'),Name = 'Test Attachment ' + i);

    attachments.add(a);
}



// Start the test, this changes governor limit context to
// that of trigger rather than test.

test.startTest();

// Insert the Attachment records that cause the trigger to execute.


insert attachments;

// Stop the test, this changes limit context back to test from trigger.


test.stopTest();


// Query the database for the newly inserted records.


List<Attachment>

insertedattachments = [SELECT Name, Description FROM Attachment WHERE Id IN:attachments];

// Assert that the Description fields contains the proper value now.



}
}

*******

 

 

Thanks for looking!

Best Answer chosen by Admin (Salesforce Developers) 
TheProjectSolutionTheProjectSolution

Sorry, there was a typo in the original solution.  I've highlighted the change here:

 

trigger Send_Email on Attachment (after insert) {
 List<Id> contractIds=new List<Id>();
   for (Attachment att : trigger.new)
   {
      contractIds.add(att.parentId);
   }
   Map<Id, Contract> contracts=new Map<Id, Contract>();
   contracts.putAll([select id, Name, contract.ContractNumber from Contract where id IN :contractIds]);
   Attachment attach = trigger.new[0];
   Messaging.SingleEmailMessage mail = new    Messaging.SingleEmailMessage();
   String[] toAddresses = new String[] {'csafford@pivotlearningpartners.org'};
   String[] bccAddresses = new String[] {'jason@theprojectsolution.com'};
   mail.setToAddresses(toAddresses);
   mail.setbccAddresses(bccAddresses);
   mail.setReplyTo('donotreply@pivotlearningpartners.org');
   mail.setSenderDisplayName('Salesforce Support');
   Contract contract=contracts.get(attach.parentId);
   if (null!=contract)
   {
      mail.setSubject('Attachment Added on : ' + contract.name + ' for ' + contract.ContractNumber);
      mail.setHTMLBody('An attachment has been added to the above Contract record.  Please visit Salesforce for details: https://na5.salesforce.com/' + contract.id);
   Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
   }

All Answers

vishal@forcevishal@force

The main purpose of a "Test Class" is to see that the code you've written is efficient, necessary and is actually being used. For example, if I have a simple login page and in the controller I have a single method LOGIN()

 

public void login()

{

     // your logic

}

 

This method will be called when the user clicks LOGIN button. So you have to mimick the same through the test class. Call this method and see if all the code inside it is getting covered (simply, we shouldn't have any code that is not getting used at all). In a way, it keeps your code clean as well.

 

In your case,

 

Attachment a = new Attachment(ParentID='800S0000000iyOp',Body =  Encodingutil.base64Decode('VGVzdA'),Name = 'Test Attachment ' + i);

Here, you have given the ParentID as a hard-coded Id. Never do this. Because when you move from one Org to another, you won't have a record having this id. Your test code fails.

 

Instead, always create all the data you need for your class inside it's Test method itself. Your test code should not depend on the data in the Org.

 

Here, you can simply create a Contract in your test method. Assign it's id as the Parent Id and your code will work just fine :)

TheProjectSolutionTheProjectSolution
Thanks for your comments! I'm sure I'm just unfamiliar with the correct syntax to use, but I tried your suggestion by preparing a test Account and Contract record within the test class, but I'm now getting the following error on the boldfaced line: " Invalid initial expression type for field Account, expecting: SOBJECT:Account (or single row query result of that type) at line 12 column 109" Here's the updated test class: ***** public class SendEmailTest { static testMethod void verifyAttachmentDescriptionsWhereOverwritten() { // Perform our data preparation. Account acc = new Account(Name = 'Test Account', Type = 'District', Account_Region__c = 'Central Valley'); Contract c = new Contract(Name = 'Test Contract', Region__c = 'Central Valley', Status = 'Draft', *Account = acc.id*, StartDate = '9/10/2012', ContractTerm = '12'); List attachments = new List{}; for(Integer i = 0; i < 200; i++){ Attachment a = new Attachment(ParentID= c.id,Body = Encodingutil.base64Decode('VGVzdA'),Name = 'Test Attachment ' + i); attachments.add(a); } // Start the test, this changes governor limit context to // that of trigger rather than test. test.startTest(); // Insert the Attachment records that cause the trigger to execute. insert attachments; // Stop the test, this changes limit context back to test from trigger. test.stopTest(); // Query the database for the newly inserted records. List insertedattachments = [SELECT Name, Description FROM Attachment WHERE Id IN:attachments]; // Assert that the Description fields contains the proper value now. } } *****
vishal@forcevishal@force

That's because you haven't inserted those Account and Contract records.

 

Account acc = new Account(Name = 'Test Account', Type = 'District', Account_Region__c = 'Central Valley'); Contract c = new Contract(Name = 'Test Contract', Region__c = 'Central Valley', Status = 'Draft', *Account = acc.id*, StartDate = '9/10/2012', ContractTerm = '12');

 

It should be :

 

Account acc = new Account(Name = 'Test Account', Type = 'District', Account_Region__c = 'Central Valley');

insert acc;

 

// Now that this account is inserted, I can associate a contract with it

Contract c = new Contract(Name = 'Test Contract', Region__c = 'Central Valley', Status = 'Draft', *Account = acc.id*, StartDate = '9/10/2012', ContractTerm = '12');

insert c;

TheProjectSolutionTheProjectSolution

Hmm, I tried adding the inserts, but I still get the same error as last time on that Account = acc.id part:

 

***

public class SendEmailTest {


static testMethod void verifyAttachmentDescriptionsWhereOverwritten()
{


// Perform our data preparation.

List<Attachment> attachments = new List<Attachment>{};

Account acc = new Account(Name = 'Test Account', Type = 'District', Account_Region__c = 'Central Valley');

insert acc;

Contract c = new Contract(Name = 'Test Contract', Region__c = 'Central Valley', Status = 'Draft', Account = acc.id, StartDate = '9/10/2012', ContractTerm = '12');

insert c;

for(Integer i = 0; i < 200; i++){

Attachment a = new Attachment(ParentID=c.id,Body = Encodingutil.base64Decode('VGVzdA'),Name = 'Test Attachment ' + i);

attachments.add(a);
}

 

// Start the test, this changes governor limit context to
// that of trigger rather than test.

test.startTest();

// Insert the Attachment records that cause the trigger to execute.


insert attachments;

// Stop the test, this changes limit context back to test from trigger.


test.stopTest();


// Query the database for the newly inserted records.


List<Attachment>

insertedattachments = [SELECT Name, Description FROM Attachment WHERE Id IN:attachments];

// Assert that the Description fields contains the proper value now.

 

}
}

***

SLockardSLockard

That's because it is expecting a whole account object as that field, so just put Account = acc and that should work.

TheProjectSolutionTheProjectSolution

Thanks, that seems to work! I made a couple additions as well to facilitate a better email notification, then I packaged the Trigger and the Test Class into a Change Set and deployed to production.

 

For any others interested in deploying this handy trigger, here's the final trigger and test class I used, bringing together Account, Contract, and Contract Attachment data. Obviously, you'll need to adjust accordingly for:

- the fields your org requires on Accounts, Opportunities & Contracts

- your desired target email addresses for the notification

- your desired copy for the email notification I've bolded the lines to adjust.

 

TRIGGER:

****

trigger Send_Email on Attachment (after insert) {
List<Id> contractIds=new List<Id>();
for (Attachment att : trigger.new)
{
contractIds.add(att.parentId);
}
Map<Id, Contract> contracts=new Map<Id, Contract>();
contracts.putAll([select id, Name, contract.ContractNumber from Contract where id IN :contractIds]);
Attachment attach = trigger.new[0];
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {'testemail1@gmail.com'};
String[] bccAddresses = new String[] {''testemail2@gmail.com'};
mail.setToAddresses(toAddresses);
mail.setbccAddresses(bccAddresses);
mail.setReplyTo('donotreply@your_company.org');
mail.setSenderDisplayName('Salesforce Support');
Contract contract=contracts.get(attach.parentId);
if (null!=contract)
{
mail.setSubject('Attachment Added on : ' + contract.name + ' for ' + contract.ContractNumber);
}
mail.setHTMLBody('An attachment has been added to the above Contract record. Please visit Salesforce for details: https://na5.salesforce.com/' + contract.id);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}

****

 

TEST CLASS:

public class SendEmailTest {

static testMethod void verifyAttachmentDescriptionsWhereOverwritten()
{

// Perform our data preparation.
List<Attachment> attachments = new List<Attachment>{};
Account acc = new Account(Name = 'Test Account', Type = 'District', Account_Region__c = 'Central Valley');
insert acc;
Opportunity opp = new Opportunity(Name = 'Test Opportunity', Account = acc, AccountID = acc.id, Amount = 5000, Revenue_Line_Items__c = 'Data Tools', Region__c = 'DRW', StageName = 'Prospecting', CloseDate = date.newInstance(2012, 10, 1));
insert opp;
Contract c = new Contract(Name = 'Test Contract', Region__c = 'Central Valley', Status = 'Draft', Account = acc, AccountID = acc.id, ContractTerm = 12, Related_Opportunity__c = opp.id);
insert c;
for(Integer i = 0; i < 200; i++){
Attachment a = new Attachment(ParentID = c.id,Body = Encodingutil.base64Decode('VGVzdA'),Name = 'Test Attachment ' + i);
attachments.add(a);
}

// Start the test, this changes governor limit context to
// that of trigger rather than test.
test.startTest();
// Insert the Attachment records that cause the trigger to execute.

insert attachments;
// Stop the test, this changes limit context back to test from trigger.

test.stopTest();

// Query the database for the newly inserted records.

List<Attachment>
insertedattachments = [SELECT Name, Description FROM Attachment WHERE Id IN:attachments];
// Assert that the Description fields contains the proper value now.

}
}

****

SLockardSLockard

I'm glad you got it to work, don't forget to mark a solution :)

TheProjectSolutionTheProjectSolution

Sorry, there was a typo in the original solution.  I've highlighted the change here:

 

trigger Send_Email on Attachment (after insert) {
 List<Id> contractIds=new List<Id>();
   for (Attachment att : trigger.new)
   {
      contractIds.add(att.parentId);
   }
   Map<Id, Contract> contracts=new Map<Id, Contract>();
   contracts.putAll([select id, Name, contract.ContractNumber from Contract where id IN :contractIds]);
   Attachment attach = trigger.new[0];
   Messaging.SingleEmailMessage mail = new    Messaging.SingleEmailMessage();
   String[] toAddresses = new String[] {'csafford@pivotlearningpartners.org'};
   String[] bccAddresses = new String[] {'jason@theprojectsolution.com'};
   mail.setToAddresses(toAddresses);
   mail.setbccAddresses(bccAddresses);
   mail.setReplyTo('donotreply@pivotlearningpartners.org');
   mail.setSenderDisplayName('Salesforce Support');
   Contract contract=contracts.get(attach.parentId);
   if (null!=contract)
   {
      mail.setSubject('Attachment Added on : ' + contract.name + ' for ' + contract.ContractNumber);
      mail.setHTMLBody('An attachment has been added to the above Contract record.  Please visit Salesforce for details: https://na5.salesforce.com/' + contract.id);
   Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
   }

This was selected as the best answer