Unit Test Scoping in Spring ’12

There are several pitfalls that developers can fall into when writing unit tests, and traditionally speaking one of the easiest to fall into can be coding with a reliance on existing data.  So for instance, you want to to prove that your SOQL is correctly getting data from your custom object – this unit test might seem relatively innocent:

List<Furniture__c> dressers = [SELECT ID, Name, Type__c from Furniture__c where Type__c =: dresserFilter LIMIT 10]; 
System.assertNotEquals(dressers.size(),0);

Which works great in the sandbox where you’ve been developing your custom furniture application with test data, but might instantly break when you move to production where no furniture data as been loaded.  More egregious scenarios may include referencing specific ID’s from one org – which won’t match up to the ID in another org.  I have myself, and I’m sure other development teams as well, found themselves at the late end of a Friday night deploy because the code I thought had excellent test coverage fails completely when moved to a different environment.

With API 24.0, or Spring ’12 to the seasonally minded, the data scope for unit testing will default so that it will not see any pre-existing data from the org.  Unit tests referring to older versions of the API will still run as expected.  With Spring ’12, the above code will fail as it doesn’t have access to the system data – but can properly be re-written to handle the situation:

List<Furniture__c> furniture = new List<Furniture__c>();
Furniture__c dresser = new Furniture__c(Name='Three Drawer',Type__c='Dresser');
Furniture__c sofa = new Furniture__c(Name='Sleeper Sofa',Type__c='Couch');
furniture.add(dresser);
furniture.add(sofa);
insert furniture;

String dresserFilter = 'Dresser';
List<Furniture__c> dressers = [SELECT ID, Name, Type__c from Furniture__c where Type__c =: dresserFilter];
System.assertEquals(dressers.size(),1);

This unit test will run as expected anywhere that has my custom object defined and since I control the test data relevant to the unit test – I can refine my system asserts for the specific use case.  If you have unit tests, however, where accessing the system data is exactly what you intend – then you can annotate the method itself with isTest(SeeAllData=true) to allow that access.  So to use the original example with API 24.0 but have it operate as before, simply add the annotation:

@isTest(SeeAllData=true)
static void testFurnitureSystemWide() {
   List<Furniture__c> dressers = [SELECT ID, Name, Type__c from Furniture__c where Type__c =: dresserFilter LIMIT 10];
   System.assertNotEquals(dressers.size(),0);
}

You can also annotate the Apex class itself if you want to apply the data access to every method within the class.  This new setup should help developers write better, more portable unit tests and aims to also improve the speed of running unit tests in general.  If you are looking for ways to easily create test data, check out Matthew Botos‘ excellent SmartFactory project – which speeds the creation of test data by using describe to fill out required fields dynamically.

Published
February 22, 2012

Leave your comments...

Unit Test Scoping in Spring ’12