Teach Me Salesforce

A community approach to learning salesforce.com

Archive for the ‘Trigger’ Category

Simplest Apex Trigger Patterns: Updates

leave a comment »

Last time I shared two very simple triggers for modifying records on Insert, which is to say, when the record is created. Which only happens once. So most of the time you probably want triggers that are working on Update since that happens a lot more often. In fact, the After Insert trigger I posted last time results in an Update event on the records; but the Before Insert does not. Why? Well it’s sort of like this: before the record is inserted, it doesn’t exist, so any change you make to it is just all wrapped up in that insert event. But after insert, the record exists in the database and any change you make to it is considered an update. But now we’re getting complex and this is suppose to be about simple patterns, so here you go…

The Before Update triggers can be written just like Before Insert triggers: if you want to set a value on a field, you just need to say SomeField = “some value”. Here we are setting the Email Opt Out checkbox (API name is HasOptedOutOfEmail) to true, so as a Boolean, we don’t need quotes, just true or false.

trigger ContactBeforeUpdate on Contact (before update) {
    For(Contact c : Trigger.new){
        c.HasOptedOutOfEmail = true;
    }
}

But After Update triggers get tricky fast. First there’s the need to use “DML” or Data Manipulation Language to perform an additional update to the just-updated record. We did this in the first post on Inserts too. And if we do another update that means the record will go through the Update events again and you’re going to get recursion – it’s going to keep trying to update over and over!

We’re going to do a very simple way of avoiding recursion, but using a condition like we did in the last post on Trigger Conditions. Since we are setting Email Opt Out to be true, our condition will only do this for Contacts on which Email Opt Out is false. So after our first update to the record, the next time through the After Update event, the record doesn’t meet the condition and no further updates to the record are executed.

trigger ContactAfterUpdate on Contact (after update){
    List<Contact> consToUpdate = new List<Contact>();
    
    for(Contact conInTrigger : trigger.new){
        if(!conInTrigger.HasOptedOutOfEmail){
            Contact c = new Contact(Id = conInTrigger.Id, HasOptedOutOfEmail = true);
        	consToUpdate.add(c); 
        }
    }
    update consToUpdate;
}

Now, let’s look at that line by line to see what’s going on:

trigger ContactAfterUpdate on Contact (after update){

This is the busiest line. It tells Apex that the code is a trigger (must be this, changing it will just break it), then it sets the name of this particular trigger (your choice, pretty much anything is allowed), then is says which object the trigger is on (but only one object!) and finally in the parentheses it tells Apex which events the trigger should be executed on (can be any or all: before insert, before update, after insert, after update, before delete, after delete & after undelete). Oh yeah, and a must-have curly bracket to start things off right.

List<Contact> consToUpdate = new List<Contact>();

This line prepares a list for the Contacts that we will want to update later. It starts out empty and we’ll add Contacts to it.

for(Contact conInTrigger : Trigger.new){

This line is the start of the “for loop” and is necessary because you might just be dealing with lots of records, not just one. Think of a situation where you’re uploading hundreds of new Contacts and need to evaluate each one independently. The for loop allows you to do that by saying “for each single thing in this collection of things, do something”. In this case it is saying: for each Contact (which I’m going to nickname “conInTrigger”) that is inserted at the same time (Trigger.new is a list of all the inserted Contacts) do what ever I tell you between this next set of curly braces.

if(!conInTrigger.HasOptedOutOfEmail){

This line is the conditional (like a Workflow filter criteria) and says “as you go through each record in the for loop, which we nicknamed ‘conInTrigger’, get the value of HasOptedOutOfEmail…and make it the opposite. That’s what the exclamation point is doing in front of conInTrigger.HasOptedOutOfEmail: changing true to false and false to true. The if() condition only proceeds if it’s true, so since we only want to change HasOptedOutOfEmail to true for records that were false, we use that value to check first. And this helps us to prevent repeated updates to the same record.

Contact c = new Contact(Id = conInTrigger.Id, HasOptedOutOfEmail = true);

Here we finally get around to updating our record. To do this we have to tell Apex that we want to work with a Contact, which we’ll call “c”. The next part if a little counterintuitive because you say first new Contact, but then in the parentheses, you give tell Apex that this Contact already has an Id. If we were actually creating a new Contact, we wouldn’t provide the Id, that would get added after its inserted. Here we use the “conInTrigger” nickname to refer to the Contact record that is in this iteration of the for loop and .Id to get its record ID. After that, we are going to set a new value, so we don’t need “conInTrigger” again, we just say that to set this Contact’s HasOptedOutOfEmail field equal to true.

consToUpdate.add(c); 

The next line adds this Contact record to the consToUpdate List we created on Line 2. If we have updated more than one Contact (maybe using Mass Edits in a List View), the for loop goes on to the next contact and keeps going until all Contacts that were updated together have been addressed.

update consToUpdate;

Our final line tells Apex that we want to update any and all Contact records that we’ve added to the consToUpdate list.

The Unit Test

Now that we’re updating records, our unit test has to first prepare some test records by inserting them and then update those same records so we can confirm that our trigger works. It’s a good idea to make sure that the trigger is actually doing the work by verifying that before the update, Email Opt Out is false and that after the update, Email Opt Out is true so we know that it was caused by the update.

 

@isTest
public class ContactAfterUpdateTest {
    public static testMethod void testContactUpdate(){
        Contact aNewContact = new Contact(LastName = 'New-Contact');
        insert aNewContact;
        
        //first let's confirm that Email Opt Out is false
        Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        System.assertEquals(false, theInsertedContact.HasOptedOutOfEmail);
        
        aNewContact.FirstName = 'Some First Name';
        update aNewContact;
        
        //now that we've updated the Contact, we'll confirm that 
        Contact theUpdatedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        System.assertEquals(true, theUpdatedContact.HasOptedOutOfEmail);
    }
}
Advertisements

Written by Always Thinkin

April 2, 2016 at 1:39 pm

Simplest Apex Patterns: Trigger Conditions

with one comment

The Trigger

Triggers fire every time for every change: inserts, updates, deletes…and it’s pretty rare that you want to execute your actions for every change. So that means you need to code in some conditions to only change the records you want changed. Just like building Workflow Rules with Rule Criteria, in Apex you’ll want to add criteria and for that you need if() statements. Probably the trickiest thing about learning to use if() statements is remember that you have to use two equals signs to say “this equals that”. Why, well, it’s just common coding syntax because you need a way to distinguish between the idea of “make something equal some value” (e.g. x = 5) and “check if two things are already equal” (e.g. a == b). And the second trickiest thing is remember whether you use single quotes, double quotes or no quotes because it depends on whether you’re criteria is checking text, numbers, IDs or booleans.

But enough chit-chat, let’s just take a look at the ContactBeforeInsert trigger we made last time with it’s new filter criteria condition that says “if the contact’s last name is ‘Hates-email'” then we want to opt them out of email.

trigger ContactBeforeInsert on Contact (before insert) {
	for(Contact c : Trigger.new){
            if(c.LastName == 'Hates-email'){
                c.HasOptedOutOfEmail = true;
        }
    }
}

Let’s break that down line-by line:

trigger ContactBeforeInsert on Contact (before insert) {

This is the busiest line. It tells Apex that the code is a trigger (must be this, changing it will just break it), then it sets the name of this particular trigger (your choice, pretty much anything is allowed), then is says which object the trigger is on (but only one object!) and finally in the parentheses it tells Apex which events the trigger should be executed on (can be any or all: before insert, before update, after insert, after update, before delete, after delete & after undelete). Oh yeah, and a must-have curly bracket to start things off right.

for(Contact c : Trigger.new){

This line is the start of the “for loop” and is necessary because you might just be dealing with lots of records, not just one. Think of a situation where you’re uploading hundreds of new Contacts and need to evaluate each one independently. The for loop allows you to do that by saying “for each single thing in this collection of things, do something”. In this case it is saying: for each Contact (which I’m going to nickname “c”) that is inserted at the same time (Trigger.new is a list of all the inserted Contacts) do what every I tell you between this next set of curly braces.

if(c.LastName == 'Hates-email'){

Now we finally get to our filter criteria conditional which says “as you go through each record in the for loop, check whether it’s last name is ‘Hates-email’ and if so, then do whatever is between the next set of curly braces. And if not, don’t do what’s in the curly braces, just go to the next record. This is where our nickname of “c” for Contacts is used because c.LastName means “this contact’s last name”. And those double equal signs say to check that the Last Name is already equal to ‘Hates-email’.

c.HasOptedOutOfEmail = true;

And at last we get around to doing something: setting Email Opt Out to true. Again we use our nickname for Contacts, “c”, to say set this Contact’s Email Opt Out field (which we refer to in Apex by the field’s API Name of HasOptedOutOfEmail) to true (see how that single equal signs is saying “make it equal…”?).

After that, we just have to close up all those curly braces we started earlier.

The Unit Test

So of course to put this in production, you need a unit test so you can get the code coverage. More importantly, you want to know that it works and for that you need to two unit tests because now we have a conditional. So we have one tests that confirms that if the last name is “Hates-email” that the Email Opt Out is set to true. And a second test that checks if last name is something else, that Email Opt Out is false.

@isTest
public class ContactBeforeInsertTest {
    public static testMethod void testHatesEmail(){
        
        Contact aNewContact = new Contact(LastName = 'Hates-email');
        
        Insert aNewContact;
        
        Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        
        System.assert(theInsertedContact.HasOptedOutOfEmail);
        
    }
    
    public static testMethod void testLikesEmail(){
        
        Contact aNewContact = new Contact(LastName = 'Likes-email');
        
        Insert aNewContact;
        
        Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        
        System.assert(!theInsertedContact.HasOptedOutOfEmail);
        
    }
}

Written by Always Thinkin

March 20, 2016 at 6:45 pm

Simplest Apex Trigger Patterns: Inserts

with 3 comments

The Trigger

Because sometimes you just want to see the basic programming patterns for Apex and reverse engineer them to fit your needs.

These two are the most basic trigger patterns. They both do the same thing: set a field’s value on a record when that record is added to Salesforce. It only happens on insert, not on update because that’s slightly less simple (and a different post). The only difference is one does it Before Insert and one does it After Insert. Why? Because they can. Maybe someday we’ll discuss here why you use one or the other but today we just want code that works, so here you go:

Trigger ContactBeforeTrigger on Contact (before insert) {
    for(Contact c : Trigger.new){
        c.HasOptedOutOfEmail = true;
    }
}

 

Trigger ContactAfterInsert on Contact (after insert) {
    List<Contact> conList = new List<Contact>();
    
    for(Contact conInTrigger : trigger.new){
        Contact c = new Contact(Id = conInTrigger.Id, HasOptedOutOfEmail = true);
        conList.add(c);    
    }
    update conList;
}

 

The Unit Test

And of course anything you want to use in Production needs a test class, so here you go, one that works for both!

@isTest
public Class TestContactTrigger{
    public static testMethod void testContactInsert(){
    
    Contact aNewContact = new Contact(LastName = 'qwerty');
    
    Insert aNewContact;
    
    Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
    
    System.assert(theInsertedContact.HasOptedOutOfEmail);
    
    }

}

Written by Always Thinkin

February 28, 2016 at 4:53 pm

Salesforce Trigger when Rollups Summaries Not Possible

with 6 comments

Master-Details relationships in Force.com  are very handy but don’t fit every scenario. For instance, it’s not possible to implement a rollup summary on formula field or text fields. Here’s a small trigger that you can use for a starter for these types of situations. The code for each class is available at GitHub for your forking pleasure.

So here’s the (not very useful) use case. Sales Order is the Master object which can have multiple Sales Order Items (detail object). The Sales Order Item has a “primary” Boolean field and a “purchased country” field. Each time Sales Order Items are inserted or updated, if the Sales Order Item is marked as “primary” then the value of “purchased country” is written into the “primary country” field on the Sales Order. I’m assuming that there can only be one Sales Order Item per Sales Order that is marked as primary. Essentially this is just a quick reference on the Sales Order to see which country is primary on any of the multiple Sales Order Items. Not very useful but illustrative.

The code is broken down into a Trigger and an Apex “handler” class that implements the actual functionality. It’s best practice to only have one trigger for each object and to avoid complex logic in triggers. To simplify testing and resuse, triggers should delegate to Apex classes which contain the actual execution logic. See Mike Leach’s excellent trigger template for more info.

SalesOrderItemTrigger (source on GitHub) – Implements trigger functionality for Sales Order Items. Delegates responsibility to SalesOrderItemTriggerHandler.

trigger SalesOrderItemTrigger on Sales_Order_Item__c (after insert, after update) {

  SalesOrderItemTriggerHandler handler = new SalesOrderItemTriggerHandler();
    
  if(Trigger.isInsert && Trigger.isAfter) {
    handler.OnAfterInsert(Trigger.new);
    
  } else if(Trigger.isUpdate && Trigger.isAfter) { 
    handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
    
  }

}

SalesOrderItemTriggerHandler (source on GitHub) – Implements the functionality for the sales order item trigger after insert and after update. Looks at each sales order item and if it is marked as primary_item__c then moves the primary_country__c value from the sales order item to the associated sales order’s primary_country__c field.

public with sharing class SalesOrderItemTriggerHandler {

  // update the primary country when new records are inserted from trigger
  public void OnAfterInsert(List newRecords){
    updatePrimaryCountry(newRecords); 
  }
  
  // update the primary country when records are updated from trigger  
  public void OnAfterUpdate(List oldRecords, 
      List updatedRecords,  Map oldMap, 
      Map newMap){
    updatePrimaryCountry(updatedRecords); 
  }
  
  // updates the sales order with the primary purchased country for the item
  private void updatePrimaryCountry(List newRecords) {
    
    // create a new map to hold the sales order id / country values
    Map salesOrderCountryMap = new Map();
    
    // if an item is marked as primary, add the purchased country
    // to the map where the sales order id is the key 
    for (Sales_Order_Item__c soi : newRecords) {
      if (soi.Primary_Item__c)
        salesOrderCountryMap.put(soi.Sales_Order__c,soi.Purchased_Country__c);
    } 
    
    // query for the sale orders in the context to update
    List orders = [select id, Primary_Country__c from Sales_Order__c 
      where id IN :salesOrderCountryMap.keyset()];
    
    // add the primary country to the sales order. find it in the map
    // using the sales order's id as the key
    for (Sales_Order__c so : orders)
      so.Primary_Country__c = salesOrderCountryMap.get(so.id);
    
    // commit the records 
    update orders;
    
  }

}

Test_SalesOrderItemTriggerHandler (source on GitHub) – Test class for SalesOrderItemTrigger and SalesOrderItemTriggerHandler. Achieves 100% code coverage.

@isTest
private class Test_SalesOrderItemTriggerHandler {

  private static Sales_Order__c so1;
  private static Sales_Order__c so2;

  // set up our data for each test method
  static {
  	
  	Contact c = new Contact(firstname='test',lastname='test',email='no@email.com');
  	insert c;
    
    so1 = new Sales_Order__c(name='test1',Delivery_Name__c=c.id);
    so2 = new Sales_Order__c(name='test2',Delivery_Name__c=c.id);
    
    insert new List{so1,so2};
    
  }

  static testMethod void testNewRecords() {
 
    Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
    soi1.Sales_Order__c = so1.id;
    soi1.Quantity__c = 1;
    soi1.Description__c = 'test';
    soi1.Purchased_Country__c = 'Germany';
 
    Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
    soi2.Sales_Order__c = so1.id;
    soi2.Quantity__c = 1;
    soi2.Description__c = 'test';
    soi2.Purchased_Country__c = 'France';
    soi2.Primary_Item__c = true;
    
    Sales_Order_Item__c soi3 = new Sales_Order_Item__c();
    soi3.Sales_Order__c = so2.id;
    soi3.Quantity__c = 1;
    soi3.Description__c = 'test';
    soi3.Purchased_Country__c = 'Germany';
    soi3.Primary_Item__c = true;
     
    Sales_Order_Item__c soi4 = new Sales_Order_Item__c();
    soi4.Sales_Order__c = so2.id;
    soi4.Quantity__c = 1;
    soi4.Description__c = 'test';
    soi4.Purchased_Country__c = 'Germany';
    
    Sales_Order_Item__c soi5 = new Sales_Order_Item__c();
    soi5.Sales_Order__c = so2.id;
    soi5.Quantity__c = 1;
    soi5.Description__c = 'test';
    soi5.Purchased_Country__c = 'Italy';
            
    insert new List{soi1,soi2,soi3,soi4,soi5}; 
     
    System.assertEquals(2,[select count() from Sales_Order_Item__c where Sales_Order__c = :so1.id]);
    System.assertEquals(3,[select count() from Sales_Order_Item__c where Sales_Order__c = :so2.id]); 
    
    System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
    System.assertEquals('Germany',[select primary_country__c from Sales_Order__c where id = :so2.id].primary_country__c);
 
  }
  
  static testMethod void testUpdatedRecords() {
    
    Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
    soi1.Sales_Order__c = so1.id;
    soi1.Quantity__c = 1;
    soi1.Description__c = 'test';
    soi1.Purchased_Country__c = 'Germany';
 
    Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
    soi2.Sales_Order__c = so1.id;
    soi2.Quantity__c = 1;
    soi2.Description__c = 'test';
    soi2.Purchased_Country__c = 'France';
    soi2.Primary_Item__c = true;
    
    insert new List{soi1,soi2}; 
    
    // assert that the country = France
    System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
    
    List items = [select id, purchased_country__c from Sales_Order_Item__c 
      where Sales_Order__c = :so1.id and primary_item__c = true];
    // change the primary country on the sales order item. should trigger update
    items.get(0).purchased_country__c = 'Denmark';
   
    update items;
    // assert that the country was successfully changed to Denmark
    System.assertEquals('Denmark',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
 
  }
  
}

Written by Jeff Douglas

August 23, 2011 at 9:00 am

Posted in Apex, Code Sample, Trigger

Apex Trigger and an Asprin can give you a dynamic approval process

with 3 comments

This post is crossed posted on my blog.

Ever come across the need of a large approval process where you do not know who the approver of the next stage will be? We all do don’t we?

In a recent project requirement, I had to work on a big approval process. My basic requirement was:

1. One approval process for child object
2. Once child is approved, it should trigger the master object approval process

Both the approval process had around 5 steps and the approvers where determined by the role hierarchy and the region of the submitter.

Yikes isn’t it?

Pre-requisites:
For this recipe, you need
1. Basic knowledge of APEX and trigger
2. Basic knowledge of Approval process
3. Aspirin (just in case you miss a step and go reverse engineering on it, its scary!!!)

Part I: The Child Object Dynamic Approval process.

Steps for Child Object Dynamic Process
1. Create two fields on the child object. These fields should not be shown on Page Layout.
i) Approval_step__c: Text field to determine which step the approval is.
ii) Next_approval__c: A user field to store the approver. (In case there are multiple approvers or parallel approver, create multiple fields)

2. Create the approval process.
i) Initial submission action will be field update: Approval_step__c=’Submitted for approval’
ii) In approver step, select related user and add the Next_approval__c
iii) In the approval Step Action, field update for Approval_step__c with the next step of Approval, e.g., ‘Regional Manager’ or maybe ‘Higher Manager’. Keep a  note of these keywords. (If the approval process is directly tied to the role hierarchy it is advisable to Update the field with next Role in approval, that way you can avoid confusion)
iv) Repeat the ii) and iii) till you finish all the approval steps.

3. Write a before update trigger on child object
i) Check if Trigger.old.Approval_step__c != Trigger.new.Approval_step__c (this is a pseudo code, you will have to iterate through trigger.new and old and compare all values, if you do not know how to do this, add a comment below)
ii) Now based on the value in Approval_step__c fetch the respective user from Role Hierarchy or anywhere else. Better option would be to use custom settings for this. Add this user in Next_approval__c
iii) Repeat step ii) till you finish all the approval steps. The final approval step would be to either empty the  Next_approval__c field or set it back to manager of the user.


4. Optional step If you need to trigger another approval process after this.
i) In the approval process, set the final approval action to ‘Approved’
ii) In the trigger if the field value is ‘Approved’ then submit the Master in Approval process using Apex Code. (There are many links which give you direct code, I might share the code later, but it has to be done in code if you want continuous approval process)

That’s it. It is easily understood that you need to repeat the whole process for master if it needs dynamic approval.
After you done everything, take a deep breath and test your code. If it doesn’t the way it should, take an aspirin and debug. Since this uses hand-shake between trigger and approval steps make sure you don’t miss out on any step.

Note: As this approval process updates back-end, the visual process manager won’t be able to visualize it.

Try it and hope you don’t need a aspirin.

P.s. This is my first post on this blog, do share your thoughts.

Written by Siddhesh Kabe

May 25, 2011 at 1:46 pm

Writing Good Test Methods

with 7 comments

Yesterday Rebecca (@sfdc_nerd) posted about her first trigger.   She did a great job and even got 100% coverage from her test class. Rebecca had actually posted this on her own blog: A force behind the force a few days earlier and I noted that she needed to do just one more thing in her test class to make it a real test. The one thing she didn’t do was check that the trigger actually created the record it was supposed to create and we sorted that out with a phone call and an email. Afterwards I started thinking that it would be good to explain what a test method really needs to be “complete” and figured I would post it here.  I have come up with a slightly contrived example below to illustrate some extra points.

I often see test classes written which achieve the necessary code coverage but aren’t exactly “tests”. Salesforce.com requires at least 75% code coverage to deploy code to a production environment. Some developers then see 75% as the goal and are happy once they cross that threshhold when they should really be shooting for 100%. Now, if you have spent any time doing development in Salesforce, you know that 100% coverage is sometimes impossible, but we should at least aim for 100%.

With that in mind, I wanted to dissect a simple test class to explain (in my opinion at least, please feel free to chime in) what you should be aiming to achieve when writing a test class.

Consider the following trigger which creates a task to call the lead when the lead is created. The due date of the lead should be 7 days from today unless a lead for a member of the very important ‘Elephantman’ family calls in. Then we will make the task due tomorrow (sorry about the formatting, wordpress doesn’t like me today).

trigger newLeadTask on Lead (after Insert) {

 List<Task> taskList = new List<Task>();
 for(Lead l : trigger.new){
 Task t = new Task(Subject = 'Contact Lead',
 WhoId = l.id,
 Status = 'Not Started', 
 OwnerId = l.OwnerId);

 if(l.LastName <> 'Elephantman'){
 t.ActivityDate = Date.today()+7;
 }else{
 t.ActivityDate = Date.today()+1;
 }
 taskList.add(t);
 }

 insert taskList; 
}

In order to test this, we must write a class which will insert a lead. Inserting the lead will cause the trigger to fire and the code will be executed.


@isTest
private class testLead{
    private static testmethod void testLeadActivity(){
        Lead l = new Lead(LastName = 'Randomname', Company = 'ABC, Inc.');
        insert l;
    }
}

Running this test we see that we get 87% coverage of our trigger. Awesome, we are ready to deploy to production because we are above 75%. Well, yes, but we are aiming for 100% here if we can get it.

We need to think of the test method as sort of a stand in for a human tester. If you asked your colleague to test this trigger, you would expect them to create a lead. Once the lead was created you would then expect them to actually check that the task was created. Well, let’s do that. After we insert the lead, let’s query for the newly created task; we should expect that 1 (and only 1) exists and that it has a date 7 days from today:

@isTest
private class testLead{
    private static testmethod void testLeadActivity(){
        Lead l = new Lead(LastName = 'Randomname', Company = 'ABC, Inc.');
        insert l;

        List<Task> taskList = [SELECT id, ActivityDate from Task WHERE whoId = :l.id];
        System.assertEquals(1,taskList.size());
        System.assertEquals(Date.today()+7,taskList[0].ActivityDate);
    }
}

The first System.assertEquals statement equates to our colleague checking that the task was actually created, the second statment checks that the date is correct. Great, so now we have a valid test but we still haven’t tested everything. The logic of the trigger says that if a lead is created for a member of the ‘Elephantman’ family, then the task due date is the next day. We expect our colleague to test both use cases, so we will do the same.

@isTest //indicates this is a test so that the code below doesn't count against our quota
private class testLead{
 private static testmethod void testLeadActivity(){
 //insert a random (non Elephantman lead)
 Lead l = new Lead(LastName = 'Randomname', Company = 'ABC, Inc.');
 insert l;

 List<Task> taskList = [SELECT id, ActivityDate from Task WHERE whoId = :l.id];
 System.assertEquals(1,taskList.size());
 System.assertEquals(Date.today()+7,taskList[0].ActivityDate);

 //insert an Elephantman lead
 Lead eMan = new Lead(LastName = 'Elephantman', Company = 'ABC, Inc.');
 insert eMan;

 List<Task> eManTaskList = [SELECT id, ActivityDate from Task WHERE whoId = :eMan.id];
 System.assertEquals(1,eManTaskList.size());
 System.assertEquals(Date.today()+1,eManTaskList[0].ActivityDate);
 }
}

When I run this test I get 100% coverage and I know that I have fully tested all use cases for this trigger. I can now deploy this to production.

Beyond being good practice, there is a very good reason to write your test classes as completely as possible. On a recent salesforce.com Webinar, I heard it mentioned that Salesforce actually runs ALL, yes ALL, test methods across all orgs before upgrading them to a new release. By writing your test methods properly, you are helping ensure nothing breaks when Salesforce updates to a new version.

Written by knthornt

May 17, 2011 at 9:30 am

Posted in Apex, Intermediate, Trigger

Trigger to create a new record

with 13 comments

Ask me a year ago, and I never thought I’d be writing triggers, but yet, here I am. I gained a bit of confidence after taking the Salesforce DEV 531 class, and that confidence then flourished with the support of this community. This blog just goes to show how many people out there are willing to help, and I definitely want to return the favor whenever possible. So here goes – the anatomy of my first trigger.

What I was looking to do was to create a new support request record (a custom object) when a new Event record of a specific type was created. Pretty straight-forward, but definitely not doable without a trigger. The final result was this:

trigger createPDtask on Event (after insert) {     
List<Support_Request__c> sr = new List<Support_Request__c>();
    for (Event newEvent: Trigger.New)
         if (newEvent.Type__c == '1. Meeting - Initial'){
                 sr.add (new Support_Request__c(
                     Name = 'New PD',
                     Task_Type__c = 'PD',
                     SFDC_Record_ID__c = newEvent.Id,
                     Rep__c = newEvent.OwnerId,
                     Event_Date__c = newEvent.ActivityDate));   
         }
   insert sr;
 }

Now for the breakdown of what I was trying to do with each step. First, I define what I want the trigger to do & when to do it.

trigger createPDtask on Event (after insert) {

The second line compiles all new records I am going to insert into a list (& therefore takes care of bulkifying my trigger).

List<Support_Request__c> sr = new List<Support_Request__c>();

Then I need to define the criteria around the event record that would cause this trigger to create a new support request.

for (Event newEvent: Trigger.New)
         if (newEvent.Type__c == '1. Meeting - Initial'){

Next, I set the details that get inserted into the new Support Request record. You can see that some are static, while a few are dynamically pulled from the event record.

sr.add (new Support_Request__c(
                     Name = 'New PD',
                     Task_Type__c = 'PD',
                     SFDC_Record_ID__c = newEvent.Id,
                     Rep__c = newEvent.OwnerId,
                     Event_Date__c = newEvent.ActivityDate));

Finally, the list gets inserted.

 }
   insert sr;
 }

As far as triggers goes, it’s pretty simple, but it was a great learning process for me to build on my own. From here, the next step is to write some test coverage, and while I thought I had it in the bag, I’m going to let Kyle share the lesson he gave me in a subsequent post.

Written by Becka

May 16, 2011 at 10:10 am

Posted in Apex, Beginner, Intermediate, Trigger

Tagged with