Preface: this post is part of the Write Your First Trigger From Start to Finish series.
Want to get a deeper understanding of our test class? Let’s do this!
Here’s the code we wrote in our previous post to test our simple trigger:
@isTest public class TestForceForecasting { static testMethod void insertNewUser() {User userToCreate = new User();// Do you recognize these fields?userToCreate.FirstName = 'David'; userToCreate.LastName = 'Liu'; userToCreate.Email = 'dvdkliu@gmail.com'; userToCreate.Username = 'sfdc-dreamer@gmail.com'; userToCreate.Alias = 'fatty';userToCreate.ProfileId = '00ei0000000rTfp';// Don't worry about these userToCreate.TimeZoneSidKey = 'America/Denver'; userToCreate.LocaleSidKey = 'en_US'; userToCreate.EmailEncodingKey = 'UTF-8'; userToCreate.LanguageLocaleKey = 'en_US';insert userToCreate;} }
All the test code does is create a new user using Apex, thereby triggering the execution of our simple trigger.
Let’s start with the outer layers. All this code does is tell Salesforce we are writing test code. These lines aren’t that important because they’ll be virtually the same no matter what you’re testing.
@isTest public class TestForceForecasting { static testMethod void insertNewUser() { . . . } }
This next line is important! This is how we create new records using Apex. You can replace the blue text with any sObject and it’ll still work.
User userToCreate = new User();
This is how we write “comments” in Apex! Salesforce skips any line of code beginning with //
// Do you recognize these fields?
This kind of code below should look familiar to you. All we’re doing is setting fields on our new user. The last line is highlighted because it’s populating a lookup field using a ProfileId – just like you would if you were doing this via Data Loader. Note that all these fields are required fields when creating a user.
userToCreate.FirstName = 'David'; userToCreate.LastName = 'Liu'; userToCreate.Email = 'dvdkliu@gmail.com'; userToCreate.Username = 'sfdc-dreamer@gmail.com'; userToCreate.Alias = 'fatty';userToCreate.ProfileId = '00ei0000000rTfp';
These are more required fields that probably look unfamiliar to you. Don’t worry about these – I promise you’ll rarely need to think about this stuff!
// Don't worry about these userToCreate.TimeZoneSidKey = 'America/Denver'; userToCreate.LocaleSidKey = 'en_US'; userToCreate.EmailEncodingKey = 'UTF-8'; userToCreate.LanguageLocaleKey = 'en_US';
Final line – this one is key! Until we explicitly insert our user into our Salesforce database using this DML code, it only exists in the matrix! ooo..
insert userToCreate;
Glossary of variables used:
@isTest – this tells Salesforce we’re writing test code
TestForceForecasting – this is the name we gave to our test class. Imagine a class to be like a folder – it helps us organize our code.
testMethod – this tells Salesforce once again that we’re doing a test. Pretty redundant huh?
insertNewUser – the name we gave our test method. If a class is like a folder, a method is like a document inside our folder that has instructions for tasks. A class can have many methods.
User – the API name of any Salesforce sObject, in this case, the standard User object.
userToCreate – the variable we named to store our user record.
’00ei0000000rTfp’ – this is the ID of the profile we’ll give to our user. Note that your org will have different IDs. 15-digit record IDs can be found in the URL of any record. Like text, IDs need to be wrapped in single quotes.
insert – the DML statement to “create” a record in an org. The record only exists in our code until we save it using DML.
Want to see this and all other code samples on this site in an actual Salesforce org? Log in to sfdc99!
Hey David, I created the following test class on the Contact email update trigger that you set as homework in the last section:
Trigger:
trigger helloWorldTrigger on Contact (before insert) {
for (Contact newContact : Trigger.new ) {
newContact.Email = ‘hello@world.com’;
}
}
Test Class:
@isTest
public with sharing class TestHelloWorld {
@isTest static void createContact() {
Contact newContact = new Contact();
newContact.FirstName = ‘Test’;
newContact.LastName = ‘Contact’;
}
}
My question is: I received 100% coverage on the test even though I never explicitly INSERTED the new contact. Can you please explain why is that the case?
Thank you for this curriculum, it’s perfect!
Excellent and honest post. I found this much useful information, as to what I was exactly searching for test information. Thanks for such post and please keep it up.
I have apex class i need to write a test class for that..
so could u please help me
Gotta read more to learn how to do test classes better!
David,
I am not clear on changing the ProfileID to get passed the “Invalid Cross Reference” error….
I simply took the ProfileID off my user record and changed the last two digits by a couple numbers and it worked.
Example myID = 1495400543954 — To run the test I set the ProfileID of ‘your’ record to 1495400543975 .
Is this how you go about it? It seems a little “hacky” to simply guess at a Profile ID that “may” work based on a current one. Is there a more clear way to convey the proper method to doing this?
You gotta use a real one! No guessing!
So navigate to a user, click on their profile, and the ID is in the URL!
I was about to ask this question, but you answered without me asking!! =)
Do we need to create the test class for anonymous code also?
Nope!
Hi David,
Your website is awesome, best I could find.
Please check my test code whenever you get time, gives me error: INVALID_CROSS_REFERENCE_KEY
@isTest
public class TestForceForecasting {
static testMethod void insertNewUser() {
User userToCreate = new User();
// Do you recognize these fields?
userToCreate.FirstName = ‘David’;
userToCreate.LastName = ‘Liu’;
userToCreate.Email = ‘dvdkliu@gmail.com’;
userToCreate.Username = ‘sfdc-dreamer@gmail.com’;
userToCreate.Alias = ‘fatty’;
userToCreate.ProfileId = ’07M28000004H05z’;
// Don’t worry about these
userToCreate.TimeZoneSidKey = ‘America/Denver’;
userToCreate.LocaleSidKey = ‘en_US’;
userToCreate.EmailEncodingKey = ‘UTF-8’;
userToCreate.LanguageLocaleKey = ‘en_US’;
insert userToCreate;
}
}
Your profileId is no good!
Hi david ,
when executing the below test class. i am getting “invalid cross reference key”
code is
@isTest
public class TestForceForecasting {
static testMethod void insertNewUser() {
User userToCreate = new User();
userToCreate.FirstName = ‘Davidsh’;
userToCreate.LastName = ‘Liush’;
userToCreate.Email = ‘dashwinsfdc99@gmail.com’;
userToCreate.Username = ‘sfdc-shdreamer@gmail.com’;
userToCreate.Alias = ‘fatty’;
userToCreate.TimeZoneSidKey = ‘America/Denver’;
userToCreate.LocaleSidKey = ‘en_US’;
userToCreate.EmailEncodingKey = ‘UTF-8’;
userToCreate.LanguageLocaleKey = ‘en_US’;
userToCreate.ProfileId=’00528000005Mf8h’;
insert userToCreate;
}
}
Check the other comments, most likely a bad ProfileId
Yeah 005 prefix is a user object prefix…
Hi david,
I have questions..for organizations that often changes validation rules and required fields, how it affects apex class?
Usually you have a test class that throws an error when a validation breaks Apex
if an Apex Class breaks by a validation, the error will be thrown to standard users, that is a technical error that cannot be read by a standard user.. so creating a apex class that throws an error readable by user is helpful and also it serves as a clue to the admin and developer that the class was break by a validation rule..is that the whole point of creating a test class if validation breaks APEX?
You’re getting it!
In most larger orgs, things like validation rules are always deployed from sandbox to production. Anytime something is deployed this way, Salesforce runs all test classes in the org. If any of them fail, especially via System.assert(), admins will get a notification and the deployment will fail. So this takes things a step further and doesn’t even allow the creation of a bad validation rule.
P.S. I had a former coworker that I nicknamed “JSON”. He was a good and smart man =)
I see,, that makes a lot of sense! In our Org right now, I am the only admin,,I’m doing all employees change request real time, live in our Production Org, except for triggers. I’m creating a test class for my simple apex trigger (case last activity date), and I noticed that what if a new validation is created, it may cause issue on my test class.
Correct me if I’m wrong…let say I deployed the trigger and test class in Production, then I created a validation that I know will affect the test class,..everything will still remain good, no errors, no issues as long as the test class is not re-ran, right?
****nice coincidence..I’m hoping I can be as smart as him :D
Your assumption is right – no issues as long as you don’t re-run your test class! But note that you will no longer be able to deploy new code unless you fix your issues, since all test classes are run when deploying.
Hey David,
I’m missing something. What ties the class to the trigger? I feel like the the trigger should be referenced in the class somewhere. I get the this example creates a new user, but how does that test the trigger? Thanks, man.
Great question!
There is no strict tie. HOWEVER, Salesforce knows that triggers x, y, and z will run whenever an user is inserted. So, all of those triggers will be “tested” by this class (and any other test classes that insert users).
David,
Thanks again for doing this.
I’ve copied your code for the “trigger ForceForecasting” and “TestForceForecast”. It worked the first time when I ran the “Run Test” I tried running it again, it then failed. So I’m assuming that once you insert, you can’t insert the same again without deleting that user. However, I tried changing the user name and details as well as ‘User userToCreate’ to ‘User userToCreateTWO’. I tried changing then the status of the trigger to try and reverse the ForecastEnabled to false to try and reset the status with the trigger. . . that lead to a really long processing time. Could you elaborate a little more on what is going on.
thank you,
Chris
Hey Chris,
Interesting problem – you are the first to have this!
Everything that happens in a test class never gets saved. So you can create 200 users in your test class and run it, but by the time the test class is totally done, it would be as if all the users never existed.
So I wouldn’t assume that’s the issue unless the error message specifically says you’re duplicating user names.
Processing time totally varies inconsistently and unpredictably, so ignore that one too!
Best thing for you is to dig into the details of the test run, see what the error message is, and take it from there. If you can’t figure it out, post the message here!
Good luck!
David
Hey DavidGetting the same Problem .
can not resolveit. Bolow is the error message
System.DmlException: Insert failed. First exception on row 0; first error: DUPLICATE_USERNAME, Duplicate Username.The username already exists in this or another Salesforce organization. Usernames must be unique across all Salesforce organizations. To resolve, use a different username (it doesn’t need to match the user’s email address). : [Username]
Try to figure this one out – I believe you can do it!
Hi David
I am much confused about my studies in salesforce. I have learnt alot in salesforce but still i confused from where do i start my practical learning. I am confused what to write whenever i open my org.
I want to some practical things but i dont know what to do for this .
can u suggest me some things to do in salesforce or on force.com plateform or in visual force so that i could catch some of the strong concepts in that .
My mail id is sg11081991@gmail.com. If you have some of sample application to be made so please provide me some of then so that I will further do new things.
please help me out in these things .
Your Sincerely
Suraj Gupta
Try out Trailhead!
hi , David
When i am testing your “TestForceForeCasting” test class
there’s a error:
System.DmlException: Insert failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: []
pls reply
thanks,
Jude
I believe some of the comments on this post (or the non-extended look) solved the problem – check em out!
I tried similar code using developer console .Its interesting to play with coding………………….
David,
I constantly read about “system.assert” method on test classes. How would you put it here in this test class?
Thanks for everything buddy!
Great question!
Check out Chapter 4 which goes into detail on how to do that!
Hi David,
Developer console for test class code coverage.
I used to look for the lines of code covered by test class from Developer Console which was pretty easy to differentiate between the code covered by test class (in Blue) and not covered (in Red). Now I am not able to find this, Please advice is this because of new release from Salesforce or anything else.
Try using the “Tests” tab in your Dev Console =)
Hi David,
You’ve been super helpful, and I appreciate all you’ve done to help…but I have a question on writing a test class for a trigger I need to deploy in my production database. Just barely getting a hold on triggers, this test class has me lost.
Basically I have a custom formula field (Business_Rating_c) that captures a value (A, B or C) based on business volume. We wanted to track changes to this value in the Account History, so I created another custom field and trigger that will capture the change to the value, update the hidden field with the previous value and update the Account history. Below is my code:
trigger BusinessRatingChange on Account (before update)
{
for(Account a:Trigger.new)
{
if(system.trigger.OldMap.get(a.Id).Business_Rating__c != system.trigger.NewMap.get(a.Id).Business_Rating__c)
{
a.Business_Rating_History__c = a.Business_Rating__c;
}
}
}
I’ve looked at your tutorials on writing a test class, but I’m lost on where to start with this one… I’ve looking through a bunch of articles, but I can’t find anything that relates to this. If this is too big of a question to answer I totally understand, but any thoughts on jump starting me in the right direction would be fantastic…thanks again!!!
Best,
Eric
Hahaha here’s what you need to do in your test class:
1. Create an Account with Business Rating A
2. Update a field on the Account so the Business Rating changes to any other value
Done!
(Also note you can do a lot of this functionality with workflows and field history tracking)!
Hi David,
iam new to this site.excellent work done by u..i am getting intrest on coding part by reading this site.
i write this forceforecast trigger and test class in my developer console but iam getting error as ”
Error Message System.DmlException: Insert failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: []
Stack Trace Class.Testforceforecasting.insertNewUser: line 21, column 1″
i changed the profile id also.
pls solve my prob..
Awesome, you’re so close!
Make sure you have the correct profile ID – it should begin with “00e”
It’s best to use a standard profile (ie System Administrator) so it guarantees the IDs are the same in prod and sandbox.
If this doesn’t solve your case, show me your code and I’ll take a look!
David
David. Thank you. I put in the Sys Admin profile and it passed.
HI David/Gousia
even i am facing same problem , i am new to the development . Please help . i wrote the forceforecast trigger and test class in my developer console but iam getting error as ” Error Message System.DmlException: Insert failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: [] Stack Trace Class.Testforceforecasting.insertNewUser: line 21, column 1″
My profile is sys admin ,.00e900000015gZ2 not sure y i am facing the issue.please help
You’re definitely using your profile ID (not mine) in this line?
userToCreate.ProfileId = ’00e900000015gZ2′;
If so, post your code!
David
David ,
Thanks me understand the the root cause of the issue . Problem solved!!!
Actually I had used your profile id in my dev environment , Now changing the profile ID to mine in the test class it worked with 100 % .But i have a couple of doubts,
since we built the trigger and tested the code in test class , Now while creating a new user in the user record , the allow forcasting checkbox is not enabled , but the trigger is used to check the forcasting checkbox checked right ???correct me if i am wrong ?
Does it work only on prod env ?
As far as my understanding is that the test class is just to test the trigger not the code to be deployed in prod as the creation of user is done through UI mostly .Please clarify David.
Thanks in advance!!!
If the trigger is deployed to production, it’ll work immediately there. But to deploy to production, you need a test class! So both must be deployed in production.
David,
Awesome site, but I have some questions.
1.) It looks like the code on this page is different from folks’ comments. I don’t see a System.assert in the “turn on forecasting for new users” code. Am I looking in the wrong place?
2.) To be clear, loops are for bulkification right?
Thanks again for this awesome site! Looking forward to meeting you in June!
Amber
Hi Amber!!! hahaha
Great observation! There are definitely multiple ways to code the exact same thing, even in a simple trigger like this one! I normally try to choose the version that I think is easiest to read and understand, even if it takes a few extra lines.
System.assert is never required in test classes (but strongly encouraged)! I avoided it just for simplicity, but future chapters definitely use it! Here’s an entire post dedicated to test class best practices (don’t worry too much about it til chapter 4+):
https://www.sfdc99.com/2013/11/02/principles-of-a-good-test-class/
Loops are primarily for bulkification but can also have uses outside of it! For example, if you ever need to calculate 1 + 2 + 3 + 4 + 5… you could always use a loop!
Hope this helps and see you soon Amber!
David
In the System.assertEquals line, should “dmcampaigns” be defined in the test? It is defined in the class we are testing, but not here. The reason I ask is that I’m getting a compiler error that says “Method does not exist or incorrect signature.” Of course when I delete that line, the test compiles very nicely :)
Sorry to be such a pest. I have been spending a lot of time trying to get this to work, and I feel like I’m so close to getting it.
No problem, I made this site to help!
We’re basically using System.assertEquals() to make sure that one “Direct Mail” campaign is returned as a result of your VF page.
Try changing the last line to this:
System.assertEquals(1, dmf.dmcampaigns.size());
You can also use the old line of code and try adding this method to your controller:
public List<Campaign> getDmcampaigns() {
return dmcampaigns;
}
Using System.assertEquals() is never required in your test class so you can actually deploy without it! It’s generally a good habit to get into though since it’ll let you know when you code starts doing unexpected things.
Post the whole code and entire error message if you’re still running into errors – happy to help!
David
I fixed a couple of things:
a) I found out that smart quotes and apex don’t mix
b) In initializing the controller, I changed it to “ApexPages.StandardsetController” because that was the way it was in the class. Here is what I have:
@isTest
public class TestDirectMailFilter {
public static testMethod void testMyController() {
Campaign c = new Campaign();
c.Name = ‘Sfdc99 for president’;
c.Type = ‘Direct Mail’;
insert c;
ApexPages.StandardsetController sc = new ApexPages.StandardsetController(c);
DirectMailfilter dmf = new DirectMailfilter(sc);
System.assertEquals(1, dmf.getDmcampaigns().size());
}
}
I’m still getting a compiler error, but it says something different:
Error: Compile Error: Constructor not defined: [ApexPages.StandardSetController].(SOBJECT:Campaign)
It’s that same line where we are defining the sc variable. What do you think the problem is?
Try this =)
@isTest
public class TestDirectMailFilter {
public static testMethod void testMyController() {
// We’re going to create a list of campaigns instead of just one
List<Campaign> campaigns = new List<Campaign>();
Campaign c1 = new Campaign();
c1.Name = ‘Sfdc99 for president’;
c1.Type = ‘Direct Mail’;
campaigns.add(c1);
// This is our second campaign. Notice that it’s not a Direct Mail campaign!
Campaign c2 = new Campaign();
c2.Name = ‘David fo shizzle’;
c2.Type = ‘Email’;
campaigns.add(c2);
insert campaigns;
// Now we call this method using the list of campaigns
ApexPages.StandardsetController sc = new ApexPages.StandardsetController(campaigns);
DirectMailfilter dmf = new DirectMailfilter(sc);
System.assertEquals(1, dmf.getDmcampaigns().size());
}
}
Since you’re using StandardSetController, the constructor expects a collection:
https://www.sfdc99.com/2013/09/28/data-collections-lists-sets-and-maps/
I also referenced this official documentation:
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_pages_standardsetcontroller.htm
This is very helpful. I had hit a wall trying to research this on my own, and you have broken through it for me. I’m still getting a compiler error: Error: Compile Error: Constructor not defined: [DirectMailfilter].(ApexPages.StandardController) at line 15 column 24
Here is the line: DirectMailfilter dmf = new DirectMailfilter(sc);
It won’t let me post the code for the page.
Hahaha it’s ok, the good news is that you don’t need to know the Visualforce at all to write a test class for it =) Only the controller!
I replied with the test class code in your first comment, check it out above!
Trying again….
//
//
//
//
//
//
//
//
//
//
//
//
Here is the code to the visual force page (in part):
Thank you! This basically filters the list on my visual force page so it only displays those campaigns where the type is “Direct Mail.” There is more than block on the page, but if you can give me some guidance on this one, I would like to try my hand at the others myself.
public class DirectMailfilter {
public List dmcampaigns {get;private set;}
public DirectMailfilter(ApexPages.StandardsetController controller) {
dmcampaigns = [SELECT StartDate, Type, Status, Name
FROM Campaign
WHERE Type = ‘Direct Mail’];
}
}
Here you go =)
The general logic and format should be good – there might be some small syntax errors cuz I’m typing this from my bed hahaha. Shouldn’t be too bad ironing them out if any.
@isTest
public class TestDirectMailFilter {
public static testMethod void testMyController() {
// First, create a campaign
Campaign c = new Campaign();
c.Name = ‘Sfdc99 for president’;
c.Type = ‘Direct Mail’;
insert c;
// Next, initialize the controller
ApexPages.StandardController sc = new ApexPages.standardController(c);
// Now initialize your class, which will call the SOQL query you made
DirectMailfilter dmf = new DirectMailfilter(sc);
// This stuff is optional – just making sure the campaign we created is queried
System.assertEquals(1, dmf.getDmcampaigns().size());
}
}
Great tutorials.
You have demonstrated writing a test for an apex trigger, but what about apex classes? I’m putting together a simple visual force page that uses extensions to the standard controllers to display campaign objects of a certain “Type.” I got everything to display the way i want it, but I don’t know how to write the tests so I can deploy what I’ve written. I figured it must be an easy solution, because they are so short. Am I wrong?
Hey Jacob,
Testing for Apex classes in theory are the same as testing an Apex trigger!
Most of the time Apex classes are slightly more complicated, so thus the test class is a little more complicated too. If possible, can you post your code here? I’ll try to help you with your test class as much as possible =)
Later chapters will cover testing Apex classes vs. triggers, so this will have to do for now!
David