Salesforce coding lessons for the 99%
Finally, Apex tutorials for point-and-click admins! Written by a self-taught Google engineer.
  • Beginner Tutorials
    • Apex
    • Certifications
    • Career Info
    • Technical Architect
    • Visualforce
    • Videos
  • Apex Academy
  • Success Stories
  • About Me
  • Misc
    • Mailbag
    • Challenges
    • Links
    • Login to my Org
Follow @dvdkliuor SUBSCRIBE!

Quiz Answers – Chapter 4

February 18, 2014

Preface – This post is part of the Write Your First Intermediate Trigger series.
View Chapter 4 quiz questions without the answers!

Chapter 4 Questions:

1. Without SOQL, what fields are available for each record entering a trigger?

Only fields directly on the record are available – related fields are not! For example, on an opportunity trigger record, Account.Industry would not be available since it exists on a related record. Another common example: AccountId would be available but Account.Id would not!

2. What character must be placed before every Apex variable used in a SOQL query?

A colon! (:) I get many emails with code that forgets to use this!

3. What does an error look like to the end user when adding an error to a record using the .addError() method?

The error looks exactly like a validation rule error.

4. Why shouldn’t a developer query for production records in a test class?

Test code runs without any existing data from both your sandbox and your production database! You should always create records from scratch, otherwise you’ll risk getting test failures down the line that’ll prevent you from deploying any code at all. For example, if someone unknowingly changed or deleted a record that was critical to your test class, you wouldn’t be able to deploy any code until that was fixed.

5. Why should every test class use System.assertEquals(), even if your code works 100% of the time?

Your code may break in the future as your org changes over time. For example, a new validation rule could prevent one of your triggers from updating certain records. System.assertEquals() will alert you of these scenarios before you find out the hard way!

6. What’s the negative test case for a trigger that divides the Amount by the number of days until the Close Date?

Two good negative test cases: (1) Close Date is today, and (2) Close Date is in the past!

7. Which is the correct way of using System.assertEquals()?
System.assertEquals(‘English Bulldog’, favoriteDog);
System.assertEquals(favoriteDog, ‘Toy Poodle’);

The first option is correct – the first argument to the assertion should be the expected result!

Chapter 4 Practice Trigger
Write a trigger on Leads that checks to see if another Lead or Contact has the exact same name. If so, populate a “Potential Lead Duplicate” or a “Potential Contact Duplicate” field. Don’t forget the test class!

trigger DetectDupes on Lead (before insert, before update) {
  for (Lead l : Trigger.new) {
    if (l.FirstName != null && l.LastName != null) {
      List<Lead> dupeLeads = [SELECT Id FROM Lead
                               WHERE FirstName = :l.FirstName
                                 AND LastName  = :l.LastName
                                 AND Id != :l.Id];      
      if (dupeLeads.size() > 0) {
        l.Potential_Lead_Dupe__c = dupeLeads[0].Id;
      } else {
        l.Potential_Lead_Dupe__c = null;
      } 
    
      List<Contact> dupeCts = [SELECT Id FROM Contact
                                WHERE FirstName = :l.FirstName
                                  AND LastName  = :l.LastName];
      if (dupeCts.size() > 0) {                                 
        l.Potential_Contact_Dupe__c = dupeCts[0].Id;        
      } else {
        l.Potential_Contact_Dupe__c = null;
      }                                         
    }
  }
}
@isTest 
public class TestDetectDupes {
  static testMethod void testDetectDupes() {
    Lead lead1 = new Lead();
    lead1.FirstName = 'Frodo';
    lead1.LastName  = 'Baggins';
    lead1.Company   = 'Shire';
    insert lead1;
    
    Contact ctc1 = new Contact();
    ctc1.FirstName = 'Frodo';
    ctc1.LastName  = 'Baggins';
    insert ctc1;
    
    Lead dupe = new Lead();
    dupe.FirstName = 'Frodo';
    dupe.LastName  = 'Baggins';
    dupe.Company   = 'Shire';
    insert dupe;
    
    dupe = [SELECT Id, Potential_Lead_Dupe__c, 
                   Potential_Contact_Dupe__c 
            FROM Lead 
            WHERE Id = :dupe.Id];
    System.assertEquals(lead1.Id, dupe.Potential_Lead_Dupe__c);
    System.assertEquals(ctc1.Id, dupe.Potential_Contact_Dupe__c);
    
    dupe.FirstName = 'Bilbo';
    dupe.LastName  = 'Baggins';
    update dupe;
    
    dupe = [SELECT Id, Potential_Lead_Dupe__c, 
                   Potential_Contact_Dupe__c 
            FROM Lead 
            WHERE Id = :dupe.Id];
    System.assertEquals(null, dupe.Potential_Lead_Dupe__c);
    System.assertEquals(null, dupe.Potential_Contact_Dupe__c);
  }
}
46 Comments
Utkarsh
December 13, 2016 @ 4:04 am

Hi David,

Thanks for all your efforts for this training.

I would like to know if we can find the duplicate leads by comparing the Lead/Contact Full Name instead of First Name and Last Name.

Is this possible?

Thanks again. UT.

Reply
radha
February 23, 2015 @ 12:31 pm

List dupeLeads = [SELECT Id FROM Lead
WHERE FirstName = :l.FirstName
AND LastName = :l.LastName
AND Id != :l.Id];
this is a” before insert “so how can we get l.id and why should we compare id s here
sorry if my doubts doesn’t make any sense

Reply
    Mayank Srivastava
    February 23, 2015 @ 12:43 pm

    Radha,
    Answer to your first question: The l.id that you see in the statement is for the record that is being updated (before update) and not for the record that is being created.
    Answer to your second question: Think of a scenario where you go in to change something on an existing lead record that isn’t a duplicate. When you save it, you would want this record to be excluded from the dupeLeads List; that is why we are doing the Id != :l.Id statement.

    Reply
      David Liu
      February 23, 2015 @ 7:37 pm

      Thank you Mayank!

      Reply
        radha
        February 28, 2015 @ 1:59 am

        but ,in the case of inserting record will it not give any error for referring l.id

        Reply
          Mayank Srivastava
          March 1, 2015 @ 11:09 am

          It wouldn’t throw any error. For newly inserted records, the Id is considered ‘Null’ so the Id != :l.Id statement for an insert in the above SOQL query would simply mean that ‘Hey, Find me all leads that have FirstName = :l.FirstName, LastName = :l.LastName AND Id != Null.

          David, please correct me if I am wrong.

          Reply
            radha
            April 6, 2015 @ 7:47 pm

            thank u

            Reply
YD
February 18, 2015 @ 2:17 am

In trigger you write the SOQL into the LOOP,
here no any solution for that to write SOQL out of the loop.???

Reply
    David Liu
    February 18, 2015 @ 8:28 pm

    Great observation! I hadn’t yet covered bulkifying so I didn’t expect it on the quiz!

    Reply
Reshmi
February 3, 2015 @ 3:15 am

Hi David,

While writing the test class for the practice trigger, I have got confused with the below line of code in you test class.

dupe = [SELECT Id, Potential_Lead_Dupe__c, Potential_Contact_Dupe__c FROM Lead WHERE Id = :dupe.Id];

here dupe supposed to be an List .. But it’s an instance of Lead Object . Its confusing. Please correct me.

Reply
    Mayank Srivastava
    February 10, 2015 @ 1:23 pm

    Hey Devi,
    We can use the dupe instance of the object to store the result of SOQL query in the above test class because a test class always run on a fresh clean database and since the query is only returning one row , we are allowed to store it in the dupe variable. However, if the SOQL query were to return multiple rows of data, we would have had to use a List variable to store the result.
    If you want, you can use a List variable in the above test class and the code will run just fine.

    Reply
J P G
December 23, 2014 @ 11:12 am

David

Are you not breaking the basic principles of a Trigger, No SOQL inside a FOR Loop here ?

Reply
    David Liu
    December 23, 2014 @ 2:22 pm

    Great observation! The only reason I did so in this quiz is because up to Chapter 4, Governor Limits haven’t been taught yet. But in a production org, yes, no SOQL in a loop!!

    Reply
      J P G
      December 23, 2014 @ 2:32 pm

      I wanted to ping your brain here, would it be a cardinal sin, if I used the for loop : Trigger new — 2 times in my trigger?

      Once to collect the values and store it in a SET or LIST then run the SOQL
      2nd Time to compare it against the Trigger.new values and do a addError against it ?

      Reply
        David Liu
        December 23, 2014 @ 4:11 pm

        I do it all the time, nothing wrong with it!

        Reply
          radha
          February 20, 2015 @ 9:34 am

          trigger.new is already a list of records then why to collect the values and store it in a record?

          Reply
Anonymous
December 18, 2014 @ 9:04 am

Hi David,

My trigger has no errors .
However, the fields are not populated. But why is it so?

trigger dupetrg on Lead (before insert,before update) {
list la= new list();
list ca=new list();
for(lead lb : trigger.new)
{
for(lead l3: la)
{
if(l3.Title==lb.Title)
l3.dupe__c= ‘checked’;

}

for(contact c3: ca)
{
if(c3.Title==lb.Title)
c3.Languages__c= ‘checked’;
}

}

}

Thanks in advance

Reply
    David Liu
    December 18, 2014 @ 8:03 pm

    Try using System.debug to see where things break down:
    https://www.sfdc99.com/2014/02/22/debug-your-code-with-system-debug/

    I recommend making your variables more descriptive too – they are very confusing to someone who is looking at your code for the first time!

    Reply
Reshmi
December 11, 2014 @ 8:47 pm

Hi David,

I tried to implement the code. But the Field Potential_Lead_Duplicate__c, is not getting populated. I created the field with Text Type. Field Type should be Okie? Tried to show up an message( l.addError(‘Update’); ) in the If part. The message is shoowing up, but not with the field update. No Validation Rule in my system for Lead. Please Help me.

Reply
    David Liu
    December 11, 2014 @ 9:50 pm

    You definitely need to read this post:
    https://www.sfdc99.com/2014/02/22/debug-your-code-with-system-debug/

    It’ll solve a lot of the issues you’re coming across, promise!

    Reply
Tony Groome
September 15, 2014 @ 3:44 am

Hi David

This is great! Learning about triggers!
The only bit I am a bit confused about is the test class where we have

dupe = [SELECT Id, Potential_Lead_Dupe__c, Potential_Lead_Contact__c
FROM Lead
WHERE Id = :dupe.Id];

How does the test class know about the Potential_Lead_Dupe__c custom field and also the Potential_Custom_Dupe__c custom field?

Does it read the assignment in the trigger where
if(dupeLeads.size() >0) {
l.Potential_Lead_Dupe__c = dupeLeads[0].Id;
} //and so on…

My guess is when you name the test class TestDetectDupes it then know to fire the DetectDupes trigger and then it all falls into place?

Thanks for all your help!
Tony

Reply
Anonymous
September 3, 2014 @ 10:44 pm

Hi David,

I am getting the error while testing the trigger-

System.AssertException: Assertion Failed: Expected: 00Q9000000UrwN0EAJ, Actual: null
Class.TestDetectDupes.testDetectDupes: line 27, column 1

Error is giving every time a new record id.

Reply
    David Liu
    September 3, 2014 @ 10:50 pm

    Make sure you don’t have any validation rules that are preventing the records from being created!

    Reply
      Anonymous
      September 3, 2014 @ 11:54 pm

      Fresh Org.No validation rule.Same error

      Reply
        David Liu
        September 4, 2014 @ 12:40 am

        Double checked in my org and it’s working!

        I recommend System.debug!
        https://www.sfdc99.com/2014/02/22/debug-your-code-with-system-debug/

        Reply
SD
August 12, 2014 @ 4:32 pm

David, I modified you trigger to the below code, and am not getting why the code coverage doesnot cover the error message alert: How to go about with this:

trigger CheckLeadDuplication on Lead (before insert,before update) {

for(lead l : Trigger.new)
{
list l1 = [select id,name from lead where name =: l.Name];
if(l1.size() >0)
{
l.adderror(‘Duplicate Lead found for’+ l.id);
}

}

Reply
    David Liu
    August 12, 2014 @ 7:59 pm

    Most likely you haven’t created any leads in your test class, which means some of the lines won’t run (because l1.size() is zero)!

    Reply
Tan
June 26, 2014 @ 5:49 am

Can you please explain me this statement

l.Potential_Lead_Dupe__c = dupeLeads[0].Id;

Reply
    David Liu
    June 26, 2014 @ 9:14 pm

    We’re setting the lead’s “Potential Lead Dupe” lookup field to the first record in our dupeLeads list. Lookup fields are always populated using IDs so we’re getting the ID of our first record in the dupeLeads list.

    Reply
Tan
June 26, 2014 @ 5:45 am

David,
just to get a clear idea what I have understood from tha last question is that if names matches you are creating a field called potential_duplicate_leads right?

because I tried to do it with custom Sobject and getting errors
saying that the field or variable does not exist…

I even tried to just paste the same code from here…same error pops up saying ” Compile Error: Invalid field Potential_Lead_Dupe__c for SObject Lead at line 9 column 9″

Reply
    David Liu
    June 26, 2014 @ 9:13 pm

    The field already exists actually (Apex almost never creates new fields!) and we’re just populating it =)

    Reply
Felix N
June 17, 2014 @ 7:34 pm

DY, my question is – since the trigger runs every time an update (or insert) is attempted on Lead, will having the line – update myLead; – within your trigger not cause the whole trigger to fire again?

What I am saying is, I think your logic is sound if you take out lines 8 and 12, because I believe they will cause your trigger to fire repetitively non-stop. Do you get it?

Reply
Nitin
June 11, 2014 @ 2:33 am

Hi David,

Great Exercise again. Hats off for your work and dedication !!

In the test class when second duplicate Lead instance ‘dupe’ is created than the trigger would have already updated the Potential_Lead_Dupe__c & Potential_Contact_Dupe__c fields for dupe instance with the proper Id, So why there is a need to explicitly assign the field value to ‘dupe’ instance through SOQL query ?

Why we can’t straight away use dupe.Potential_Lead_Dupe__c & dupe.Potential_Contact_Dupe__c for assertion ?

Reply
    David Liu
    June 11, 2014 @ 5:14 pm

    Even though the changes were saved and made in the database, we won’t have all the latest values available in the code. That’s why we query first to get them!

    The only exception to this is IDs – they become available to you right away in your code without need a SOQL query!

    (You can test all this out by removing the SOQL and testing!)

    Reply
      Nitin
      June 11, 2014 @ 9:21 pm

      Thank you very much for clarifying this doubt David :)

      Reply
rohith bolineni
May 21, 2014 @ 11:31 am

Hey David.. :)

the answer to question 2 has a typo.. It is a colon and not a semi colon.

Reply
    David Liu
    May 22, 2014 @ 5:23 pm

    GREAT CATCH!!! Updated! Thank you!

    Reply
Vaishali Singh
April 30, 2014 @ 6:24 am

HI David,
Why there is a need to again create the instance of lead, as we have already created lead1. ~We could have updated the lead1 record??

Reply
    David Liu
    April 30, 2014 @ 6:59 pm

    Great question!

    We need two Leads because we check for any duplicate Leads and Contacts!

    So we need one Lead as our original, one Lead to be the Lead version of the duplicate, and one Contact to be the Contact version of the duplicate!

    Reply
DY
April 1, 2014 @ 3:18 pm

Since your query is inside the for loop, it could run into governor limits with mass processing, right? If that’s the case, would the be a way to bulkify it? I’m not quite sure if this would work, especially with the two consecutive for loops nested within the other one.

trigger IdentifyDupes on Lead (before insert, before update) {
List allLeads = [SELECT Id, FirstName, LastName FROM Lead] ;
List allContacts = [SELECT Id, FirstName, LastName FROM Contact] ;
for (Lead myLead : Trigger.new) {
For(Lead l : allLeads) {
If(l.FirstName == myLead.FirstName && l.LastName == myLead.LastName && l.id != myLead.id) {
myLead.Potential_Lead_Duplicate__c = l.id;
Update myLead;}
For(Contact c: allContacts) {
If(c.FirstName == myLead.fullName && c.LastName == mtLead.LastName) {
myLead.Potential_Contact_Duplicate__c = c.id;
Update myLead;}
}
}

Reply
    David Liu
    April 1, 2014 @ 6:51 pm

    Excellent observation!! You are ready for Chapter 5 =)

    (The entire chapter is dedicated to showing you how to bulkify!)

    Make sure to try the quiz to make sure you’re learning correctly!
    David

    Reply
    Felix N
    June 14, 2014 @ 9:47 pm

    Hello DY,

    I like the logic you applied in your trigger, and thanks for sharing it here. I noticed though that you have a line of code updating Lead (update myLead;) from within your trigger – which is itself a ‘before update’ trigger. It appears to me that the ‘update myLead’ line will trigger your trigger all over again, resulting in an infinite back and forth cycle of trigger calls. What do you think?

    Reply
      David Liu
      June 17, 2014 @ 6:16 pm

      It’s ok to have it in the test class (since that’s independent from the trigger) but you are right if it was in the trigger we’d have a major problem!

      (Let me know if I’m not seeing the update call that you’re referring to!)

      Reply
        Felix N
        June 17, 2014 @ 6:35 pm

        Hi David,

        I was referring to lines 8 and 12 in DY’s version of the trigger. I numbered the lines so it’s easier to reference.

        Thanks.

        1. trigger IdentifyDupes on Lead (before insert, before update) {
        2. List allLeads = [SELECT Id, FirstName, LastName FROM Lead] ;
        3. List allContacts = [SELECT Id, FirstName, LastName FROM Contact] ;
        4. for (Lead myLead : Trigger.new) {
        5. For(Lead l : allLeads) {
        6. If(l.FirstName == myLead.FirstName && l.LastName == myLead.LastName && l.id != myLead.id) {
        7. myLead.Potential_Lead_Duplicate__c = l.id;
        8. Update myLead;}
        9. For(Contact c: allContacts) {
        10 If(c.FirstName == myLead.fullName && c.LastName == mtLead.LastName) {
        11 myLead.Potential_Contact_Duplicate__c = c.id;
        12. Update myLead;}
        13. }
        14. }

        Reply
          DY
          June 17, 2014 @ 6:56 pm

          Hmm, now that I’m looking at it again, I would probably add the duplicate lead to a list that stores leads that need to be updated, and then make one call at the end to update the entire list. Not sure if that gets at the issue that you’re pointing out, though.

          Reply
          David Liu
          June 17, 2014 @ 8:51 pm

          You are right, great call!!

          Since there’s already an “update” (implied by the before update), you don’t need to have the line to update any leads =) It will do so automatically!

          David

          Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *


*

*

Theme: Simple Style by Fimply