A unit test is typically described as:
a method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine if they are fit for use.
In Salesforce, unit tests are really much more similar to integration tests.
Integration testing is the phase in software testing in which individual software modules are combined and tested as a group.
The reason I say this is due to the fact that Salesforce currently lacks a good mock framework. With a very positive change in Spring ’12 that forces the isolation of test data, the developer now has to generate the entire data model whenever they want to write a unit test. This is extremely important to know as this will drive how we structure our unit tests. Let’s take a look at a scenario.
Suppose you have some method that determines the difference in the amount of Widget__c
‘s a Foo__c
has to the Count_One__c
or Count_Two__c
of a Stub__c
based on the Use_Count_One__c
flag. I want to keep this simple enough to get the concepts across but complex enough to show some of the better real world scenarios so bear with me here.
public class ExampleController{ public Integer differenceInFooWidgetsAndStubCount(Id fooId, Id stubId){ Foo__c foo = [ SELECT Id, (SELECT Id FROM Widget__r) FROM Foo__c WHERE Id = :fooId ]; Stub__c stub = [ SELECT Id, Count_One__c, Count_Two__c, Use_Count_One__c FROM Stub__c WHERE Id = :stubId ]; if(stub.Use_Count_One__c){ return foo.Widget__r.size() - stub.Count_One__c; }else{ return foo.Widget__r.size() - stub.Count_Two__c; } } }
Basic Structure of The Test
Now, something important to note is that with starting with Summer ’13 all unit tests should be in their own class. Finally! This is great news as it is starting to force developers to write cleaner code (simply by delineating between production code and unit tests). So, knowing this, we need to make a test class. I typically like to use the name of the original class appended with Test
. Let’s take a look at a class with some pseudo code for a test method.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ // Generate test model // Perform test logic // Assert logic } }
First thing to notice is we used the @isTest
annotation. By marking our test with that annotation, it forces all code in that class to be run in the testing context only. It also doesn’t count against your code coverage limits or against your Apex code limits. Now, looking down at our test method you see instead of annotating it @isTest
I went ahead and used testMethod
instead. The two designations are interchangable and I could have easily have written the method as:
@isTest static void testDifferenceInFooWidgetsAndStubCountOne(){ // Logic }
The benefit of using @isTest is that you can provide the SeeAllData="true"
flag to it to access your organization’s data (although only do this if you absolutely have to). I typically prefer testMethod
so for this example we will stick with that.
Setting up the Data Model
Let’s go ahead and start setting up more of our code. Let’s generate our test model so we have some data to work with.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Foo__c foo = new Foo__c(); insert foo; List<Widget__c> widgets = new List<Widget__c>(); for(Integer i = 0; i<3; i++){ widgets.add(new Widget( Foo__c = foo.Id, Foo__r = foo )); } insert widgets; Stub__c stub = new Stub__c( Count_One__c = 1, Count_Two__c = 2, Use_Count_One__c = true ); insert stub; // Perform test logic // Assert logic } }
This is going to set up the test data we need by insert
ing a Foo__c, 3 Widget__c
‘s, and a Stub__c
. While this will work, it really isn’t the most efficient way to handle this. First of all, this logic will need to be copy and pasted across every test method. That is a huge issue if for some reason down the line you add a required field to Foo__c for instance and now you have to add that field to every instance of Foo__c you are creating across your entire class. The obvious solution is to encapsulate this logic into methods.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Foo__c foo = generateFoo(); List<Widget__c> widgets = generateWidgets(foo, 3); Stub__c stub = generateStub(true); // Perform test logic // Assert logic } private Foo__c generateFoo(){ Foo__c foo = new Foo__c(); insert foo; return foo; } private List<Widget__c> generateWidgets(Foo__c foo, Integer count){ List<Widget__c> widgets = new List<Widget__c>(); for(Integer i = 0; i<count; i++){ widgets.add(new Widget( Foo__c = foo.Id, Foo__r = foo )); } insert widgets; return widgets; } private Stub__c generateStub(Boolean useCountOne){ Stub__c stub = new Stub__c( Count_One__c = 1, Count_Two__c = 2, Use_Count_One__c = useCountOne ); insert stub; return stub; } }
Awesome! We have our generation code separated out for this class. Any time we need to generate a Foo__c for this class, it is in one spot. We even made some of our other fields dynamic (such as adding a count
or the useCountOne
parameters). We know certain tests are going to want to different values for some of those fields. By parameterizing them, we can make them different per test scenario.
With that said, we still have an issue. Foo__c
is going to be used all throughout our org. Even if it isn’t now, it has the potential to be used elsewhere. Do we really want separate methods in all of our test classes for generating a Foo__c
? It is the same scenario that we ran into above. To solve that issue, we will move our data setup out into a TestUtilities
class and access all of the data generation through static
methods.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Foo__c foo = TestUtilities.generateFoo(); List<Widget__c> widgets = TestUtilities.generateWidgets(foo, 3); Stub__c stub = TestUtilities.generateStub(true); // Perform test logic // Assert logic } }
@isTest public class TestUtilities{ public static Foo__c generateFoo(){ Foo__c foo = new Foo__c(); insert foo; return foo; } public static List<Widget__c> generateWidgets(Foo__c foo, Integer count){ List<Widget__c> widgets = new List<Widget__c>(); for(Integer i = 0; i<count; i++){ widgets.add(new Widget( Foo__c = foo.Id, Foo__r = foo )); } insert widgets; return widgets; } public static Stub__c generateStub(Boolean useCountOne){ Stub__c stub = new Stub__c( Count_One__c = 1, Count_Two__c = 2, Use_Count_One__c = useCountOne ); insert stub; return stub; } }
We really cleaned up our test now. By refactoring we were able to make our test class much cleaner and provide a framework to generate our test data easily. In this scenario it isn’t truly necessary, but I would like to take this one step even further. By utilizing the generic sObject, we can actually limit our DML statements as well.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(true); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); // Perform test logic // Assert logic } }
@isTest public class TestUtilities{ public static Foo__c generateFoos(Integer count){ List<Foo__c> foos = new List<Foo__c>(); for(Integer i = 0; i<count; i++){ foos.add(new Foo()); } return foos; } public static List<Widget__c> generateWidgets(List<Foo__c> foos, Integer count){ List<Widget__c> widgets = new List<Widget__c>(); for(Foo__c foo:foos){ for(Integer i = 0; i<count; i++){ widgets.add(new Widget( Foo__c = foo.Id, Foo__r = foo )); } } return widgets; } public static List<Stub__c> generateStubs(Boolean useCountOne, Integer count){ List<Stub__c> stubs = new List<Stub__c>(); for(Integer i = 0; i<count; i++){ stubs.add(new Stub__c( Count_One__c = 1, Count_Two__c = 2, Use_Count_One__c = useCountOne )); } return stubs; } public static Map<String, List<sObject>> generateTestModel(Boolean useCountOne){ List<sObject> objectsToAdd = new List<sObject>(); List<Foo__c> foos = generateFoos(1); List<Stub__c> stubs = generateStubs(useCountOne, 1); objectsToAdd.add(foos); objectsToAdd.add(stubs); insert objectsToAdd; objectsToAdd.clear(); List<Widget__c> widgets = generateWidgets(foos.get(0), 3); objectsToAdd.add(widgets); insert objectsToAdd; objectsToAdd.clear(); Map<String, List<sObject>> objectMapping = new Map<String, List<sObject>>(); objectMapping.put('Foo__c', foos); objectMapping.put('Stub__c', stubs); objectMapping.put('Widget__c', widgets); return objectMapping; } }
Now, I want you to take a moment to really understand what we just did here. The leap we just made isn’t the easiest to understand right away. What we did was utilized List
s of generic sObject
s to limit our DML operations. In this scenario, we dropped our total DML operations from 3 down to 2. As mentioned before, this may be a bit overkill for this particular situation, but this is exponentially beneficial after more and more pieces get added to the data model. This is being put in place for the future. We refactored everything to generate List
s of objects as well which will be useful when we test triggers/other functionality. Using this new method, we were able to remove the concept of the Widget__c
from our test completely. We don’t need to pass any of the Widget__c
‘s info the controller, so we don’t need to bring it into the test at all.
Writing Test Logic
Now that our test data is set up, we can go ahead and start writing some test logic. In this scenario, we will want to create two tests. We need a test to cover the first path of the if statement as well as the else condition.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(true); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); // Assert logic } static testMethod void testDifferenceInFooWidgetsAndStubCountTwo(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(false); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); // Assert logic } }
There are a few important things to note here. First, note that we have Test.startTest()
and Test.stopTest()
. These methods are in place specifically to validate your governor limits. Any code that is run between these two calls with run with their own set of governor limits. This ensures that large data model setups do not effect the governor limits set in place that your code would typically run into by just being run. It is important to notice that both of these methods have separate parameters based into the data model generation, forcing the flow to enter either the positive if condition or falling through to the else condition. Next, notice how we are setting our result back onto a variable in our test. We will be using that in our final step, assertions.
Assertion
The final, and most crucial, part of any test is the assertion. This is the part of your test that will tell you if the method you ran is actually doing what you expect it to do. The sad truth about Apex is that this piece is completely optional to deploy to production. The code we have written already has 100% code coverage. The issue is the only thing the unit tests we wrote validates is that there were no exceptions thrown when the code was run. That simply isn’t very useful. While this is acceptable in Salesforce’s eyes to go to production, it is completely unacceptable in the eyes of any professional programmer. You will be responsible for validating your code is working properly. It is up to you to verify that the controller logic you wrote 6 months ago is still working properly after your latest change to a service it calls. To do this, you absolutely must write assertions. Am I making my point? Always, always write assertions!!
Now that we have that out of the way, Salesforce offers a few different assertion methods on the System
class. In this scenario, we will just use the System.assertEquals
. Let’s take a look at it in action in our example.
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(true); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); System.assertEquals(2, difference, 'The difference should be 2 when Count_One__c is used'); } static testMethod void testDifferenceInFooWidgetsAndStubCountTwo(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(false); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); System.assertEquals(1, difference, 'The difference should be 1 when Count_Two__c is used'); } }
The way System.assertEquals
works is it takes two required parameters, expected and actual (in that order) as well as an optional error message. In the above scenario, when Use_Count_One__c
is true
, the expected difference
is 2. If difference
comes back as anything but 2, a fatal error will be thrown and the unit test will fail. This is what really wraps the entire test up. It validates that what we expect the method to return actually gets returned. It validates that our code is operating exactly the way we planned.
Another important thing to note is that this logic has been broken up into two separate tests. We could have easily written this logic as a single method, like:
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStub(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(true); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); System.assertEquals(2, difference, 'The difference should be 2 when Count_One__c is used'); Map<String, List<sObject>> objectMappingTwo = TestUtilities.generateTestModel(false); Foo__c fooTwo = objectMappingTwo.get('Foo__c').get(0); Stub__c stubTwo = objectMappingTwo.get('Stub__c').get(0); Test.startTest(); ExampleController controllerTwo = new ExampleController(); Integer differenceTwo = controllerTwo.differenceInFooWidgetsAndStubCount(fooTwo.Id, stubTwo.Id); Test.stopTest(); System.assertEquals(1, differenceTwo, 'The difference should be 1 when Count_Two__c is used'); } }
This would not be a good idea. It invalidates the idea of a “unit” test. We want to make our unit tests as small as possible. Test the smallest piece of functionality you can and have as many tests as possible. This not only keeps the code cleaner but it makes it very easy to identify any issues when a very specific unit test fails.
Wrap Up
I hope some of this has been helpful. From my point of view, the most complex part of the process is the data setup. In non-ideal scenarios where you have to insert 20+ object types to run one of your test methods, the things I have detailed out here on generating your data model can be extremely beneficial. It can help performance in respect to the speed of running your unit tests and the ability of making changes to your data model. The simplest part of any unit test is the actual logic being run. It should be as simple as possible to ensure you have a very specific piece of functionality being tested. This will give you a better insight on what is actually happening when a unit test fails. Finally, the most important part is the assertion. Let me repeat, the most important part is the assertion! I honestly can not stress this enough. The assertion is the actual check that validates your code is working the way you expected. Without that, all you validate is that no exceptions were thrown.
Good luck out there with your testing! Our final code base looks like:
The Actual Code – ExampleController
public class ExampleController public Integer differenceInFooWidgetsAndStubCount(Id fooId, Id stubId){ Foo__c foo = [ SELECT Id, (SELECT Id FROM Widget__r) FROM Foo__c WHERE Id = :fooId ]; Stub__c stub = [ SELECT Id, Count_One__c, Count_Two__c, Use_Count_One__c FROM Stub__c WHERE Id = :stubId ]; if(stub.Use_Count_One__c){ return foo.Widget__r.size() - stub.Count_One__c; }else{ return foo.Widget__r.size() - stub.Count_Two__c; } } }
Test Class – ExampleControllerTest
@isTest public class ExampleControllerTest{ static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(true); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); System.assertEquals(2, difference, 'The difference should be 2 when Count_One__c is used'); } static testMethod void testDifferenceInFooWidgetsAndStubCountTwo(){ Map<String, List<sObject>> objectMapping = TestUtilities.generateTestModel(false); Foo__c foo = objectMapping.get('Foo__c').get(0); Stub__c stub = objectMapping.get('Stub__c').get(0); Test.startTest(); ExampleController controller = new ExampleController(); Integer difference = controller.differenceInFooWidgetsAndStubCount(foo.Id, stub.Id); Test.stopTest(); System.assertEquals(1, difference, 'The difference should be 1 when Count_Two__c is used'); } }
Test Utility – TestUtilities
@isTest public class TestUtilities{ public static Foo__c generateFoos(Integer count){ List<Foo__c> foos = new List<Foo__c>(); for(Integer i = 0; i<count; i++){ foos.add(new Foo()); } return foos; } public static List<Widget__c> generateWidgets(List<Foo__c> foos, Integer count){ List<Widget__c> widgets = new List<Widget__c>(); for(Foo__c foo:foos){ for(Integer i = 0; i<count; i++){ widgets.add(new Widget( Foo__c = foo.Id, Foo__r = foo )); } } return widgets; } public static List<Stub__c> generateStubs(Boolean useCountOne, Integer count){ List<Stub__c> stubs = new List<Stub__c>(); for(Integer i = 0; i<count; i++){ stubs.add(new Stub__c( Count_One__c = 1, Count_Two__c = 2, Use_Count_One__c = useCountOne )); } return stubs; } public static Map<String, List<sObject>> generateTestModel(Boolean useCountOne){ List<sObject> objectsToAdd = new List<sObject>(); List<Foo__c> foos = generateFoos(1); List<Stub__c> stubs = generateStubs(useCountOne, 1); objectsToAdd.add(foos); objectsToAdd.add(stubs); insert objectsToAdd; objectsToAdd.clear(); List<Widget__c> widgets = generateWidgets(foos.get(0), 3); objectsToAdd.add(widgets); insert objectsToAdd; objectsToAdd.clear(); Map<String, List<sObject>> objectMapping = new Map<String, List<sObject>>(); objectMapping.put('Foo__c', foos); objectMapping.put('Stub__c', stubs); objectMapping.put('Widget__c', widgets); return objectMapping; } }
in generateTestModel line 43 should be
insert objectsToAdd;
rather than
insert stubs;
I think
Thanks Patrick. You are definitely correct. I updated the post based on your feedback. Sometimes pseudo code can have issues like that lol!
Great article Jesse ! Somebody from Developer Force and I’m really glad they did !
Hi
An excellent illustration of a test class. Thanks a lot. I will understand it over time. I just want to confirm few things from you.
1. Now people are writing constructor in test class and putting all material for the class in it.
2. is there any updates of illustration you provided after Winter’14 Release.
Thanks a lot again for your efforts. Looking forward to have such things in future too. Keep it up.
I’m getting an error of: “Incompatible element type LIST for collection of SObject” in the TestModel code. What am I missing?
public static Map<String, List> generateTestModel(){
List objectsToAdd = new List();
List accounts = generateAccounts(1);
objectsToAdd.add(accounts);
insert objectsToAdd;
objectsToAdd.clear();
List contacts = generateContacts(accounts.get(0),3);
List opportunities = generateOpportunities(accounts.get(0), 3);
objectsToAdd.add(contacts);
objectsToAdd.add(opportunities);
insert objectsToAdd;
objectsToAdd.clear();
Map<String, List> objectMapping = new Map<String, List>();
objectMapping.put(‘Account’, accounts);
objectMapping.put(‘Contact’, contacts);
objectMapping.put(‘Opportunity’, opportunities);
return objectMapping;
}
Hi Paul –
I am having the same issue.
The problem is the example code is trying to add lists of SObjects to an SObject List.
I think the correct syntax for defining the objectsToAdd list in this scenario would be:
List<List objectsToAdd = new List<List>();
which would allow you to add your list of foos and list of widgets to the objectsToAdd list…
This then causes another error when you try the insert:
DML requires SObject or SObject list type: LIST<LIST>;
Which I think is saying that Apex will let you insert an sObject or a list of sObjects, but not a list of sObject lists.
The fixes I’ve come up with so far don’t seem very efficient, but I’ll share them anyway:
1. For each sObject list you want to add to the objectsToAdd list run for loop to iterate over the list and add the sObjects individually:
for(Foo__c aFoo:foos)(
objectsToAdd.add(aFoo);
)
2. Create the list of Lists as shown above and then iterate over that list and insert each member list individually (this seems like the opposite of what the TestUtilities class is about since it creates a lot of insert calls rather than reducing them)
I’ll take a look into this. Potentially something changed but this used to work just fine.
Sorry, the Syntax got messed up in my reply:
The correct syntax to allow for adding the foo list to the objectsToAdd list would be;
List<List> objectsToAdd = new List<List>();
The answer came from @Bob_Buzzard in the Developer forums. Short answer, you have to cast the object to an sObject, just like good ol’ Java.
The following should process fine:
public static Map<String, List> generateTestModel(Boolean useCountOne){
List objectsToAdd = new List();
List foos = generateFoos(1);
objectsToAdd.add((LIST)foos);
insert objectsToAdd;
return objectMapping;
}
For more details on the solution, see:
https://developer.salesforce.com/forums/ForumsMain?id=906F0000000Ad3jIAC
Back in the test method, you will also need to recast the sObject:
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Map<String, List> objectMapping = TestUtilities.generateTestModel(true);
Foo__c foo = (FOO__C) objectMapping.get(‘Foo__c’).get(0);
}
For more details about casting in Apex:
https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_casting.htm
@Isaac
Thanks for that update! I modified my list add method from .add() to .addAll() and cast everything to sObject and back as described in Bob’s post and it seems to be working great.
I have question on how to expand this easy to understand example to a more complex use case.
Does anyone have a suggestion on how to populate data for more complex objects? Some of my customs have several required fields and those fields often impact how the code under test should behave.
My first though was I could just create the test data objects with a hardcoded value in the test utilities class and then modify the fields in the controller test class with the values needed for the test. The problem I see with this is that is requires an update call for each object that gets modified in each test….which again seems like the opposite of the point of the test utilities concept.
I am now thinking it may make sense to have all of my “generateSObject” methods accept a map of required field names and the desired value as an argument, something like:
public static CustomObject__c generateCustomObject(Integer count, Map){
//code to assign map values to CustomObject field values
}
Does this seem like a reasonable approach? Is there a better way to do this?
Thanks,
Adam