Teach Me Salesforce

A community approach to learning salesforce.com

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);
    }
}

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

Apex Homework Assignment 1: Control Flow – If Statements and For Loops

leave a comment »

I ran a bi-weekly Apex Workshop for the NYC Force.com Developer User Group from April 2014 to March 2015 and developed practice code to accompany the topics we covered. The focus was on helping Admins practice their Apex skills so they can become Developers. And now I’m sharing it with everyone so you can practice the skills you’ve learned in Trailhead or in classes or from reading documentation!

All the code is designed to work in any Developer Edition org without creating any new records or metadata. So you can just cut and paste this right into a new Apex Class to get started. Once you’ve copied it and saved it, start reading through the inline comments which provides some helpful notes about common Apex code conventions (and patterns you can re-use). Look for the “FIX ME!” comments which prompt you to uncomment certain lines and edit them so that the code compiles (saves) and that running the Unit Test will succeed.

This assignment depends a lot on looking at the Debug Logs to see the results from System.debug() statements – an important exercise in familiarizing yourself with the Logs!

And if you want to learn more about the Control Flow in this assignment, check out:
Cooking With Code: Conditional Statements in Apex (and Java)
Cooking with Code: Oh … for the Love of FOR Loops
at womencodeheroes.com!

 

/* This Apex Class is a Unit Test Class and all the code can be checked by running the test.
* Copy this into a new Apex Class named "ControlFlow" and save it.
* As you read through it, look for "FIX ME!" and make the changes suggested.
* If your changes are correct all the tests (System Asserts) will pass.
* 
* You can also see what's happening in the Logs by opening a Log and checking the Debug Only box
* you can compare the line numbers in the Log to the line number in the Class to find a specific
* debug entry.
*/

@isTest
public class ControlFlow {
    static testMethod void logic(){
        //prep a Map first so we have something to work with.
        Map<String, Decimal> metricMap = new Map<String, Decimal>{'Kilo' => 1000, 'Milli' => .001};
            
            //IF statements. These are just like Workflow Rules.
            if(true){
                //And here is your Workflow Action in between the curly brackets
                System.debug('Do Something!');
            }
        
        //you can condense it to one line if there's only one action.
        if(true) System.debug('Still do something');
        
        //prep a true/false variable for the next step.
        Boolean myBool = true;
        
        //use "else" when you only want the action to happen when your if case is false
        //This is what Workflow Rules can't do: actions for multiple results from one rule
        if(myBool){
            System.debug('The Boolean was true');//only runs if myBool is true
        } else {
            System.debug('The Boolean was false');//only runs if myBool is false
        }
        System.debug('The Boolean doesn\'t matter here');//This line will always run
        
        //you can string multiple condition cases with "else if"
        if(myBool){
            System.debug('The Boolean was true');
        } else if(!myBool){
            System.debug('The Boolean was false');
        } else {
            System.debug('Whoa! there was no value');
        }
        
        //FIX ME!: Try changing myBool to false and confirm that the conditions cascade to the next condition
        //FIX ME!: Try not setting myBool to any value and see what happens...
        
        //And just so you know it exists, there is the ternary operator for if-else statements
        //This has to be used with variable assignment, so it's use is limited but elegant
        //Here, we set a String of "true" or "false" depending on the Boolean value
        String theAnswer = (myBool ? 'true' : 'false');
        
        //FIX ME!: Google "Apex Boolean Methods" to find how to convert a String to a Boolean and then assert that myBool equals theAnswer
        //Since we cannot directly compare a String to a Boolean, you have to create a new Boolean "b" with the conversion.
        //Boolean b = ???;
        //System.assertEquals(myBool, b);        
        
        //Most of the time we're comparing two things in our conditions
        //You have to get used to typing "==" when you think "equals" in conditions
        if(metricMap.size() == 2){
            System.debug('Let\'s Add More!');
        }
        
        //FIX ME!: Now that you know about using "==" to compare values, 
        //rewrite this conditional block to accomodate null values.
        
        String myPicklist = 'true';
        //if(myPicklist){
        //System.debug('The value is true');
        //} else if(myPicklist){
        //System.debug('The value is false');
        //} else if(myPicklist){
        //System.debug('The picklist was empty');
        //} else {
        //System.debug('The value was neither true nor false');//what do you have to set myPicklist to to get this result? 
        //}
        
        //Any method that returns a boolean can be used in the condition clause.
        //With Maps, a common pattern is to check for a value's existence,
        //and if it's not there, put it in the Map.        
        if(metricMap.containsKey('Mega')){
            System.debug('Mega is already there!');
        } else {
            metricMap.put('Mega', 1000000);
        }
        System.assertEquals(metricMap.get('Mega'), 1000000);
        
        //FIX ME!: Add "Micro" to the metricMap so the following System Assert passes
        if(metricMap.containsKey('Micro')){
            //System.debug('Micro is already there!');
        } else {
            //metricMap.put('', .000001);
        }
        //System.assertEquals(metricMap.get(''), );
        
        //let's prep some Booleans to play with comparison operators
        Boolean x = true;
        Boolean y = false;
        
        //Conditionals can contain chains of variables (or boolean methods).
        //Comparison operators follow the rules of logic you use everyday in Reports, Views and Formulas
        if(x && y) System.debug('True AND False evaluates to False');
        if(x && !y) System.debug('True AND NOT False evaluates to True');
        if(x || y) System.debug('True OR False evaluates to True');
        if(!x || y) System.debug('Not True OR False evaluates to False');
        if(!(x && y)) System.debug('True AND False evaluates to False, BUT we re-evaluate that cumulative False to True with NOT(!)');
        
        //FIX ME! Add in a third Boolean "z" to get these conditions to be true
        Boolean z = true;
        //if(x && y) System.debug('Now it evaluates to True!');
        //if(!x || y) System.debug('Now it evaluates to True!');
        
        
        
        ////////////////
        // FOR LOOPS  //
        ////////////////
        
        //For Loops allow you to work with multiple variables or objects
        //There wasn't a good equivalent in the UI until Process Builder came along
        //In Apex, they are an essential control for, well, everything!
        
        //First variation: For each item in a List or Set, do something
        Set<String> metricSet = metricMap.keySet();//Remember these are: ['Mega', 'Micro', 'Milli']
        
        for(String s : metricSet){//for every string in metricSet do something...
            System.debug(s);
        }
        
        //FIX ME! Now that we've looped through the keys in metricSet, loop through the values
        List<Decimal> metricValues = metricMap.values();
        //for(){
            //system.debug();
        //}
        
        //Second variation: for each record returned by a SOQL query, do something.
        for(User u : [Select Name from User]){
            System.debug(u.Name);
        }
        
        //FIX ME! Write a for loop that retreive all the Profile Names and prints them in a System Debug
        //Note that in Unit Tests without SeeAllData = True, Users and Profiles are still accessible along with a few other special Objects
        
        //for(Profile p : []){
        	//System.debug();
        //}
        
        
        //Third variation: repeat the loop as many times as instructed
        //This uses a special format common to many languages of establishing:
        //1. An Entry Condition: Integer i = 0 (arbitrary, but 0 is useful when working with list indices)
        //2. A Comparison: i < 5 (means "as long as")
        //3. An Incrementor: i++ (add 1 to i each loop)
        for(Integer i = 0 ; i < 5 ; i++){
            System.debug('i now equals: ' + i);
        }
        
        List<String> names = new List<String>{'Dave', 'Jim', 'Sara', 'George'};
            String d = names[0];
        
        //A very frequent pattern is repeating the loop for each item in a list using the list.size() method
        for(Integer i = 0; i < names.size(); i++){
            System.debug('Value at index ' + i + ' is ' + names[i]);//we can use i to retrieve each item in the list in order
        }
        
        //FIX ME! Make a for loop of 5 Integers that adds each Integer to itself
        //use a system debug to output the result like "1 + 1 = 2"
        //you have to use 3 variations on + to do this: 
        //1. + to concatentate two items
        //2. + as a string to print out the plus sign (and later the equals sign)
        //3. + as a mathematical operator to add the two Integers
        //for(){
            //System.debug();
        //}
        

            
        //Loop-di-Loop. You can embed loop within loops.
        
        //prep some units of measurement
        List<String> units = new List<String>{'meter', 'watt', 'gram'};
        //The first iteration will loop through all the values in the inner loop first
        //and then proceed on to the next item in the outer loop
        for(String u : units){
            for(String m : metricMap.keySet()){
               System.debug('A ' + m + u + ' is ' + metricMap.get(m) + u + 's');//We use m to get the string and the metric multiplier from the Map!
            }
        }
    }
}

Written by Always Thinkin

June 23, 2015 at 8:42 am

Posted in Uncategorized

Apex Homework Assignment 0: Collections – Lists, Sets and Maps

with 3 comments

I ran a bi-weekly Apex Workshop for the NYC Force.com Developer User Group from April 2014 to March 2015 and developed practice code to accompany the topics we covered. The focus was on helping Admins practice their Apex skills so they can become Developers. And now I’m sharing it with everyone so you can practice the skills you’ve learned in Trailhead or in classes or from reading documentation!

All the code is designed to work in any Developer Edition org without creating any new records or metadata. So you can just cut and paste this right into a new Apex Class to get started. Once you’ve copied it and saved it, start reading through the inline comments which provides some helpful notes about common Apex code conventions (and patterns you can re-use). Look for the “FIX ME!” comments which prompt you to uncomment certain lines and edit them so that the code compiles (saves) and that running the Unit Test will succeed.

And if you want to learn more about the Collections in this assignment, check out Cooking with Code: Amuse-bouche* of Apex Collections (Lists, Sets, and Maps) at womencodeheroes.com!

Good luck – and let me know what you think!

/* This Apex Class is a Unit Test Class and all the code can be checked by running the test.
* Copy this into a new Apex Class named "Collections" and save it.
* As you read through it, look for "FIX ME!" and make the changes suggested.
* If your changes are correct all the tests (System Asserts) will pass.
* 
* You can also see what's happening in the Logs by opening a Log and checking the Debug Only box
* you can compare the line numbers in the Log to the line number in the Class to find a specific
* debug entry.
*/

@isTest
private class Collections {
    static testMethod void Lists(){
        //LISTS ARE AN ORDERED COLLECTION OF DATA
        
        //How to create a List with some pre-defined values
        String someword = 'rope';
        List<String> threeStrings = new List<String>{'rope', 'twine', 'thread'};
            System.assertEquals(3, threeStrings.size());//.size() is an Apex List Method - Google & learn more!
        System.assertEquals('rope', threeStrings[0]);//List items start with Index 0, that's what [0] means
        
        //How to create a List with a controlled number of items
        String[] oneWord = new String[1];//here [1] means "room for only 1 item in the list"
        oneWord[0] = 'able';//here [0] means put the word in Index 0 (the first item in the list)
        //oneWord[1] = 'baker';//Won't Work! Try uncommenting an running the test. The List was configured for only 1 item
        oneWord.add('charlie');//Will Work! The .add() List Method automatically expands the List
        oneWord[1] = 'baker';
        System.debug(oneWord);//System.debug will show up in the Logs so you can check the value at any time
        System.assertEquals(2, oneWord.size());
        
        //How to create an empty List with no limit on items
        List<String> noStrings = new List<String>();
        System.assert(noStrings.isEmpty());
        noStrings.add('rock');
        System.assertEquals(1, noStrings.size());
        
        //FIX ME! Using the values in the first List we made, uncomment the two lines below and get them to pass
        System.assertEquals('rope', threeStrings[0]);
        //System.assertEquals('', threeStrings[1]);
        //System.assertEquals('', threeStrings[2]);
        
        //Lists can also be Lists of Lists. The insides lists don't have to have a name, you can reference them with the Index number
        List<List<String>> numberNames = new List<List<String>>();
        numberNames.add(new List<String>{'zero', 'one', 'two', 'three', 'four'});
        numberNames.add(new List<String>{'cero', 'uno', 'dos', 'tres'});
        numberNames.add(new List<String>{'ling', 'yi', 'er'});
        System.assertEquals(3, numberNames.size());
        System.assertEquals('four', numberNames[0][4]);
        System.assertEquals('tres', numberNames[1][3]);
        //FIX ME! Uncomment and add the correct value to the make it pass 
        //System.assertEquals('', numberNames[2][1]);
        
        //FIX ME! Using the data in numberNames uncomment the lines below and get them to pass
        //System.assertEquals(4, numberNames[1].size());
        //System.assertEquals(3, numberNames[2].size());
        
        //FIX ME! Uncomment and add the correct value to make it pass
        //System.assertEquals('', numberNames[1][1] + ' ' + numberNames[1][2] + ' ' + numberNames[1][3]);
        //You can use System.debug(numberNames[1][1] + ' ' + numberNames[1][2] + ' ' + numberNames[1][3]); to see what it is if you need help
        
    }
    
    static testMethod void Sets(){
        //SETS ARE AN UNORDERED COLLECTION OF UNIQUE DATA
        
        Set<String> planets = new Set<String>{'Mars', 'Earth', 'Pluto'};
            System.assertEquals(3, planets.size());
        
        Set<String> months = new Set<String>();
        months.add('June');
        months.add('July');
        months.add('June');//You can try to add this again, but it will have no effect
        System.assertEquals(2, months.size());
        System.assert(months.contains('July'));// contains() is a useful Set Method that returns true/false
        
        Set<Id> Ids = new Set<Id>();//IDs are very useful in sets. Adding the same idea twice to a List will fail, but not Sets!
        Id me = UserInfo.getUserId();
        Ids.add(me);
        System.assertEquals(1, Ids.size());
    }
    
    static testMethod void Maps(){
        //MAPS ARE NAMED COLLECTIONS OF DATA
        Map<String, String> fieldValues = new Map<String, String>{'Name' => '2U, Inc.', 'Website' => '2u.com', 'Phone' => '301-892-4350'};
            System.assertEquals(3, fieldValues.size());
        
        Map<Integer, String> numbers = new Map<Integer, String>();
        System.assert(numbers.isEmpty());
        numbers.put(1, 'One');
        numbers.put(2, 'Two');
        numbers.put(3, 'Three');
        System.assert(numbers.containsKey(1));
        
        System.assertEquals('One', numbers.get(1));
        
        //FIX ME! Using the data in the numbers map, uncomment the lines below and get them to pass
        //System.assertEquals('', numbers.get(2));
        //System.assertEquals('Three',);
        
        System.debug(numbers.keySet());//keySet() returns the Keys from your Map as a Set.
        //FIX ME! Populate the Set called 'myKeys' with Integers to get the assert to pass
        //Set<Integer> myKeys = ;
        //System.assertEquals(numbers.keySet(),myKeys);
        
        System.debug(numbers.values());//values() returns the Values from your Map as a List.
        //FIX ME! Populate a List called 'myValues' that you can use to get the assert to pass
        //List<String> myValues = ;
        //System.assertEquals(numbers.values(), myValues);
        
        //FIX ME! On the Map Methods page in the Apex docs, find the method that allows you to remove an item from the Map
        // and get the assertion to pass
        //numbers.??????(2);
        //System.assertEquals(2, numbers.size());
        
        //FIX ME! And now find and use the method to remove all the values from the Map
        // and get the assertion to pass
        //numbers.?????();
        //System.assert(numbers.isEmpty());
    }
    
    @isTest(SeeAllData=True)
    static void SOQLandCollections(){
        //This only works when this test Class is set to SeeAllData=True which is an Apex Annotation that exposes real data in unit tests. 
        List<Account> accts = [Select ID from Account];
        System.debug(accts.size());
        System.debug(accts);
        //System.debug(accts[0].Name);//This won't work because Name was not acquired in the query.
        
        accts = [Select ID, Name from Account];
        System.debug(accts.size());
        System.debug(accts);
        System.debug(accts[0].Name);//Now it works because we included Name in our query.
        
        //Populating a Map from a SOQL Query is also easy, although not necessarily intutive
        //Note that you have to "cast" the query results (a List) to a Map
        Map<Id, Account> acctMap1 = new Map<Id, Account>([Select ID from Account]);
        System.debug(acctMap1);
        
        
        
        //COMMON ROOKIE ERROR
        //Using Lists to update record instead of Maps
        List<Account> acctList = [Select ID, Name, Industry, Ownership from Account];
        List<Account> acctListToUpdate = new List<Account>();
        
        //{There might be some logic here that determines Type must be updated on a record}
        acctList[0].Type = 'Retailing';
        acctListToUpdate.add(acctList[0]);
        
        //{There might be some other logic here that determines Rating must be updated on the same record}
        acctList[0].Rating = 'Super Hot';
        acctListToUpdate.add(acctList[0]);
        
        System.debug(acctList[0]);
        
        System.debug(acctListToUpdate);
        /* The update below compiles: you can save this class with it uncommented, but the class
		* fails when we run the test because we added the same record to the list twice.
		* Uncomment it and see what happens. Then comment it out again and move on to the better solution.
		*/
        //update acctListToUpdate;//Fails with "Duplicate id in list" exception.
        
        
        //Let's try the same thing, but using a Map to hold the records to be updated
        Map<ID, Account> acctMapToUpdate = new Map<Id, Account>();
        
        //We make the same field/values assigment as before on the same record
        acctList[0].Type = 'Retailing';
        //but now we put the record in a Map instead of a list
        acctMapToUpdate.put(acctList[0].Id, acctList[0]);
        
        //And we do it again for the Rating Field
        acctList[0].Rating = 'Super Hot';
        //when we put it a second time, the Map recognizes that the ID already exists, and just 
        //amends the data in the Map with the new information 
        acctMapToUpdate.put(acctList[0].Id, acctList[0]);
        
        system.debug(acctMapToUpdate);
        update acctMapToUpdate.values();
        
        //Here's an unexpected pattern for SOQL queries with Parent & Child records
        //You would expect that it might be a List<List> pattern so you could call Contacts
        //with acctsCons[0][0] but instead you must use dot notation to reference the sub-lists
        List<Account> acctCons = [SELECT Name, (SELECT FirstName, LastName FROM Contacts) FROM Account];
        system.debug(acctCons[0].Contacts[0].FirstName);
        
    }
    
}

Written by Always Thinkin

June 21, 2015 at 11:16 am

Posted in Uncategorized

Unit Tests: Not Just for Dev – Idea 1

leave a comment »

Unit tests offer much more than just the means to get 75%+ code coverage, and they can be used to protect more than just Apex. Read the intro to this series in Idea 0.

Our first example is the simplest: protecting fields that you don’t want deleted. A use case for this is External Integrations that rely on Salesforce fields. If someone tries to delete the fields, you want to ensure that they are alerted to the impact. By including the fields Current Generators and SIC Code in the example Unit Test below, an attempt to delete the fields will throw an error referencing the unit test.

@isTest
private class UnitTest1 {
    static testMethod void protectFields(){

    /*Create a new Lead, populating any Fields you need to protect
    * In this case, we want to ensure Current Generators and SIC Code cannot be deleted
    * or the field type changed by including them in this Apex Unit Test
    */
    Lead l = new Lead(Company = 'Test Lead',
                      LastName = 'Lead Last Name',
                      CurrentGenerators__c = 'Generator X',
                      SICCode__c = '1234-ABC');
    insert l;
    }
}

Let’s describe what’s happening here.

We start off with the all-important @isTest flag. This tells Salesforce that the Apex Class contains only test Methods. That means nothing you do in the Class will be saved to the server, and will not delete or change anything on the server. It also means you cannot use most records that exist on the server, so you have to create your own test records.

@isTest means that Salesforce will recognize it as a test Class, and when you Run All Tests, it will know to run this test too.

Next is the Class Name, UnitTest1 and its access modifier is set to “private”. Access modifiers are irrelevant for Unit Tests, private is the default.

The actual test Method here is protectFields(). Your test exists between the curly braces {}. Here you must create your test records and perform any actions you want tested. The critical item here is “testMethod” which ensures that Salesforce recognizes the method as performing tests. Without this, Salesforce will not report a test failure regardless of the outcome.

In this case we are creating a Lead. We are using standard Apex code to create a Lead, to populate fields on that Lead, and finally to use DML to insert the Lead. Required fields and Validation Rules will get enforced when you create the Lead, so be sure to include any fields affected by them.

Once this Unit Test is saved to the server, any attempt to delete the field or change its data type will be blocked with a reference to this Unit Test.

For this limited scenario, that’s all we need!

Written by Always Thinkin

August 3, 2014 at 3:53 pm

Posted in Apex, Code Sample, Intermediate

Tagged with ,

Unit Tests: Not Just for Dev – Idea 0

leave a comment »

This post is kicking off a series of posts about how Salesforce Admins & Developers can use Unit Tests for more than just testing Apex. The target audience is Admins who haven’t written Unit Tests. The ideas for Unit Tests here are intended to be easily copied and modified by anyone with System Admin access, a Sandbox or Developer Edition Org and a need to add a little more protection to their hard-won successes in building their org.

Unit Tests are designed to test Apex Triggers and Classes. They are required for deploying them to Production, but more important they are an essential tool to ensure that your logic, both Apex and declarative, does what it’s supposed to do, and nothing more or less.

But how else can they be useful?

Protection Integrations: Many Salesforce Orgs are now integrated with other applications and rely on the fields in Salesforce to, well, exist. And not to change or get locked down. Unit tests can be used to protect all the fields an integration uses against deletion, against changes to field type or restrictions by Field-level Security or Validation Rules.

Testing Declarative-only Apps: All those Clicks-not-Code components can add up to a complex series of Workflow Rules, Formula Fields and Validation Rules where you’ve got an application that is vulnerable to even slight changes. Unit tests can be used to ensure that a series of related Rules and Fields perform as expected, and that changes don’t disrupt the outcome.

And that application’s complexity can become difficult to test when there are hundreds of combinations of fields and their values. Throw in the ability to mass edit records in Views or Data Loader and you’ve got hours of manual tests to perform. Unit Test can be written to try all the combinations you want to throw at it, for hundreds of records.

The Ideas and Unit Tests in this series are all written using the sample data and metadata from the Developer Edition Orgs. Where necessary, I’ll add some new Workflow Rules, Validation Rules and Fields to come up with viable scenarios. If you don’t already have a Developer Edition org, you can get one (or one hundred) free Orgs at developer.force.com.

Because it’s so handy, we’ll use the Developer Console to write and run the Unit Tests, but you can also use the Force.com IDE for Eclipse, MavensMate or even the declarative interface.

You won’t need to know much Apex. I’ll attempt to explain the parts that are not intuitive, or just advise you to do-as-I-do if the explanation won’t help (or I don’t know either).

So put your pencils down, Testing time is up: Idea 1

Written by Always Thinkin

August 3, 2014 at 3:28 pm

Posted in Apex, Intermediate, Validation, Workflow

Tagged with ,