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 widgets = new 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 inserting 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 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 generateWidgets(Foo__c foo, Integer count){
List widgets = new List();
for(Integer i = 0; icount 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.

[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Foo__c foo = TestUtilities.generateFoo();
List widgets = TestUtilities.generateWidgets(foo, 3);
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 generateWidgets(Foo__c foo, Integer count){
List widgets = new List();
for(Integer i = 0; irefactoring 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.

[java]
@isTest
public class ExampleControllerTest{
static testMethod void testDifferenceInFooWidgetsAndStubCountOne(){
Map> 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
}
}
[/java]
[java]
@isTest
public class TestUtilities{
public static Foo__c generateFoos(Integer count){
List foos = new List();
for(Integer i = 0; i generateWidgets(List foos, Integer count){
List widgets = new List();
for(Foo__c foo:foos){
for(Integer i = 0; i generateStubs(Boolean useCountOne, Integer count){
List stubs = new List();
for(Integer i = 0; i> generateTestModel(Boolean useCountOne){
List objectsToAdd = new List();
List foos = generateFoos(1);
List stubs = generateStubs(useCountOne, 1);
objectsToAdd.add(foos);
objectsToAdd.add(stubs);
insert objectsToAdd;
objectsToAdd.clear();

List widgets = generateWidgets(foos.get(0), 3);
objectsToAdd.add(widgets);
insert objectsToAdd;
objectsToAdd.clear();

Map> objectMapping = new 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 Lists of generic sObjects 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 Lists 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> 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> 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
}
}
[/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> 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> 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’);
}
}
[/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> 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> 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’);
}
}
[/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> 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> 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’);
}
}
[/java]

Test Utility – TestUtilities

[java]
@isTest
public class TestUtilities{
public static Foo__c generateFoos(Integer count){
List foos = new List();
for(Integer i = 0; i generateWidgets(List foos, Integer count){
List widgets = new List();
for(Foo__c foo:foos){
for(Integer i = 0; i generateStubs(Boolean useCountOne, Integer count){
List stubs = new List();
for(Integer i = 0; i> generateTestModel(Boolean useCountOne){
List objectsToAdd = new List();
List foos = generateFoos(1);
List stubs = generateStubs(useCountOne, 1);
objectsToAdd.add(foos);
objectsToAdd.add(stubs);
insert objectsToAdd;
objectsToAdd.clear();

List widgets = generateWidgets(foos.get(0), 3);
objectsToAdd.add(widgets);
insert objectsToAdd;
objectsToAdd.clear();

Map> objectMapping = new Map>();
objectMapping.put(‘Foo__c’, foos);
objectMapping.put(‘Stub__c’, stubs);
objectMapping.put(‘Widget__c’, widgets);
return objectMapping;
}
}
[/java]

Advertisement

Go to Smartblog Theme Options -> Ad Management to enter your ad code (300x250)

Comments are closed.