Proper Unit Test Structure in Apex
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.
[java]
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;
}
}
}
[/java]
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.
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
// Generate test model
// Perform test logic
// Assert logic
}
}
[/java]
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:
[java]
@isTest
static void testDifferenceInFooWidgetsAndStubCountOne(){
// Logic
}
[/java]
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.
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Foo__c foo = new Foo__c();
insert foo;
List
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
}
}
[/java]
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.
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Foo__c foo = generateFoo();
List
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
List
for(Integer i = 0; iuseCountOne
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.
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Foo__c foo = TestUtilities.generateFoo();
List
Stub__c stub = TestUtilities.generateStub(true);
// Perform test logic
// Assert logic
}
}
[/java]
[java]
@isTest
public class TestUtilities{
public static Foo__c generateFoo(){
Foo__c foo = new Foo__c();
insert foo;
return foo;
}
public static List
List
for(Integer i = 0; i
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Map
Foo__c foo = objectMapping.get(‘Foo__c’).get(0);
Stub__c stub = objectMapping.get(‘Stub__c’).get(0);
// Perform test logic
// Assert logic
}
}
[/java]
[java]
@isTest
public class TestUtilities{
public static Foo__c generateFoos(Integer count){
List
for(Integer i = 0; i
List
for(Foo__c foo:foos){
for(Integer i = 0; i
List
for(Integer i = 0; i
List
List
List
objectsToAdd.add(foos);
objectsToAdd.add(stubs);
insert objectsToAdd;
objectsToAdd.clear();
List
objectsToAdd.add(widgets);
insert objectsToAdd;
objectsToAdd.clear();
Map
objectMapping.put(‘Foo__c’, foos);
objectMapping.put(‘Stub__c’, stubs);
objectMapping.put(‘Widget__c’, widgets);
return objectMapping;
}
}
[/java]
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.
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Map
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
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
}
}
[/java]
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.
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Map
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
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’);
}
}
[/java]
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:
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStub(){
Map
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
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’);
}
}
[/java]
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
[java:Actual Code]
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;
}
}
}
[/java]
Test Class – ExampleControllerTest
[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Map
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
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’);
}
}
[/java]
Test Utility – TestUtilities
[java]
@isTest
public class TestUtilities{
public static Foo__c generateFoos(Integer count){
List
for(Integer i = 0; i
List
for(Foo__c foo:foos){
for(Integer i = 0; i
List
for(Integer i = 0; i
List
List
List
objectsToAdd.add(foos);
objectsToAdd.add(stubs);
insert objectsToAdd;
objectsToAdd.clear();
List
objectsToAdd.add(widgets);
insert objectsToAdd;
objectsToAdd.clear();
Map
objectMapping.put(‘Foo__c’, foos);
objectMapping.put(‘Stub__c’, stubs);
objectMapping.put(‘Widget__c’, widgets);
return objectMapping;
}
}
[/java]