Demystifying SeeAllData in Unit Tests
Writing Unit Tests is a responsibility we as developers should not take lightly. It is our duty to not only ensure pure code coverage, but also to ensure that our code does what we intend it to do! One way of making sure our unit tests are solid and reproducible is by not relying on data in an organization. This gets you into trouble – we’ve all been there – when you try to deploy your Apex class and associated unit test to production but the data you were relying on in the Sandbox does not exist in Production. Or you try to deploy your Apex class and Unit Test to production and there are too many records returned by a query, hitting a governors limit.
Thankfully, by default (since API Version 24.0), Salesforce hides existing data in the org, making our jobs of writing repeatable unit tests easier by not relying on data in the org, but rather by creating our own test data. Furthermore, Salesforce helps us out by ensuring that any DML operations performed in a unit test are not committed to the database – everything is rolled back at the end of the transaction. So we do not have to worry about deleting all the test records we need to create when SeeAllData=false
.
This article will help you figure out what Data and Metadata SeeAllData
affects. The three most important things to know when writing Unit Tests for Apex Classes in Salesforce are as follows.
- What Is Visible with
SeeAllData=false
- What is NOT Visible with
SeeAllData=false
- When to use
SeeAllData=true
As stated above, by default, Salesforce hides data in your organization from Unit Tests. This is a really helpful feature most of the time as it enables you to test your classes and triggers in isolation. It forces the tests to not be dependent on transient data. It also ensures that your test is repeatable.
For instance, let’s say you are testing an Opportunity Trigger that creates a Renewal Opportunity, you need to first create the source Opportunity Object and its related Opportunity Line Items in your test rather than relying on existing records. Yes, it may be tedious to do this, but much safer and more resilient. It is best practice to write a Test Utility class that will create common Objects you need across multiple unit tests (such as User, Account, Contact, Opportunity, and Custom Objects).
1. What is Visible With SeeAllData=false
By default, when executing Unit Tests in Salesforce, the data in the list below is visible. Largely speaking, these items are considered your Salesforce org’s Metadata. The items marked with an asterick (*) are special in that they are not strictly Metadata, however they are useful when writing Unit Tests and do not cause issues being exposed to the Unit Tests.
- Custom Setting definitions – includes Field Names and Types
- SObject definitions for both Standard and Custom Objects – includes Field Names and Types, Lookup Relationships, Master-Detail Relationships, Picklist Values
- AsyncApexJob
- CronTrigger
- ApexClass
- ApexTrigger
- ApexComponent
- ApexPage
- Organization
- Picklist values (ie Opportunity Stage Names)*
- Record Types*
- Email Templates*
- User Profile.* To get standard user Profile in a unit test, use:
Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
- Standard Price Book* (as of Summer 2014 release). To get it, use:
Test.getStandardPricebookId()
2. What is NOT Visible With SeeAllData=false
By default, when executing Unit Test in Salesforce, the following data is NOT visible. This constitutes the actual live data that makes up your Salesforce org. The one that always surprises me is the first one – Custom Settings records. I consider this to be static configuration data so I would expect it to be available to the Unit Test as it is similar to the category of data that is visible. However, it is not Metadata but in fact is considered data so dummy values must be created for testing.
- Custom Settings records – The actual records/data.
- Standard Object records. No existing records of Standard Objects such as Opportunity, Contact, Account, etc. are visible. It is also worth pointing out that the following records are visible.
- Custom Price Books (as noted above, Standard Price Book is now exposed, however all other Price Books are not visible).
- Products
- Price Book Entries (Since there are no Products, there are no Price Book Entries records. Also important to note that while the standard Price Book is available, its Price Book Entries and associated Products are not. They must be created in your test class.).
- Custom Object records. Just like Standard Object Records, no existing records of your Custom Objects are visible. For instance, all existing records for a custom Object named Project are not visible.
- ActivityHistory (more on this later)
3. When to use SeeAllData=true
So now that we have established that SeeAllData=true
is evil and should be avoided at all costs; Do not pass go, do not collect $200, you get the picture. Let’s talk about some real life scenarios when you do need to use it. Remember that SeeAllData
can be applied at both the Class AND Method level so if you must use it, then apply it at the most granular level possible – ie confine it to a single test method rather than the entire class.
- If you need access to a custom Price Book in your Unit Test, then you have two choices. Either re-create the custom Price Book in the same way that you create other dummy data for testing. Or, if necessary, use
SeeAllData=true
to access the Custom Price Book. - This is an edge case without a doubt, but something I have come across as I’m sure others have as well. If there is a different version of a managed package (or even a difference in Configuration) between Production and Sandbox Org such that the version in production introduces a validation rule, it may mean you can’t create an instance that will work in both orgs. And you are not at liberty to update the sandbox to match production, so what do you do? Instead of creating an instance of this Object, query for an existing instance. So you will have your instance to work with. Your ability to do true functional testing is limited in this case, but it will enable you to get pass your tests, get required code coverage and deploy your change set.
- Access to ActivityHistory records. Strangely enough, if you are writing a Unit Test that creates an Activity on some Object, say Opportunity, then you would expect to see the ActivityHistory record in the database when you perform a query from your test code. However, the ActivityHistory records are not returned without
SeeAllData=true
. being set to true. This seems inconsistent as we can query for the Opportunity and find it in the database. Turning onSeeAllData=true
solves the problem.
While at first it may seem tedious and time-consuming to write effective Unit Tests without SeeAllData=true
, all it takes is one or more botched-up deployments to make you see the light and to be willing to change your ways. Salesforce is doing us a favor by hiding the pre-existing data from us. Yes, it will take longer to initially write your Unit Test, but your test will be more robust and maintainable going forward.