Testing Best Practices in Salesforce
When migrating your apex code from a Sandbox to a Production environment, the Force.com platform mandates you to ensure that your code passes minimum 75% code coverage requirement while unit testing. This has been a best practice in the coding world for quite a long time for obvious reasons. Salesforce has made it a compulsion on their platform so that your code doesn’t misbehave once deployed to Production environment with live data.
In the ensuing write-up we will explore Testing Strategies – Common Pitfalls and Best Practices.
- Test Classes run in parallel
When tests are initiated from the Salesforce user interface (including the Developer Console), test classes are run in parallel. Individual test methods inside test classes are run serially, but not necessarily sequentially. Now, although the test classes run in parallel by default to speed up test run time, sometimes parallel test execution could lead to data contention issues; typically if the tests are accessing your org data. See the following code sample:
Test Class 1
1A @isTest (seeAllData = true) 2A public class TestClass1 { 3A private static testMethod void setAccount() { 4A Account a= //Select a single account 5A a.Name= ‘ABC’; 6A update a; 7A // check logic 8A } 9A } |
Test Class 2
1B @isTest (seeAllData = true) 2B public class TestClass2 { 3B private static testMethod void setAccount2() { 4B Account a= //Select a single account 5B a.Name= ‘XYZ’; 6B update a; 7B // check logic 8B } 9B } |
In the above code, if lines 4A and 4B try to unfortunately lock the same account record from your database, this could lead to UNABLE_TO_LOCK_ROW error. To avoid this, firstly try to avoid using your org data as test data. It is a best practice to create your own test data with the @testSetup for each test class. This data would be isolated per class and would only exist for the duration of the test run. Another option is to disable the parallel test execution by going to Setup|Develop|Apex Test Exectution|Options…|Disable Parallel Apex Testing. Finally you could refactor the code to have both the test methods in the same class.
- Code coverage calculation
Code coverage percentage is calculated whenever tests are executed. The general formula for code coverage percentage calculation is:
Code coverage percentage = Number of covered lines / (Number of covered lines + Number of uncovered lines)
Tests are run in two situations, first is when you explicitly run them through the Developer Console or from the Setup menu, and second, implicitly when you are trying to deploy your code to another org. The code coverage percentage is calculated in both these cases but they are differently treated. In the first case the code coverage percentage is persisted to a code coverage table for the benefit of the Testing tools. In the second case it isn’t stored anywhere. The reason for this is that during deployment, apart from the apex code being deployed, there could also be some metadata being modified in the target org (for example, a lookup relationship being changed to Master-Detail). These metadata changes if unsuccessful (some child records not having parents while converting from lookup to M-D, point in case) could potentially rollback the deployment. Now, if the code coverage percentage was stored in this case and the rollback happened then the code coverage percentage could be considering some executable lines of the apex code which now don’t exist since they were part of this deployment which has now rolled back.
In short, the code coverage percentage that is stored when you manually run tests isn’t referred to when deploying code, instead, the platform implicitly recalculates it on the fly.
Things that are not considered as a part of the code coverage percentage calculation include: Comments and blank lines, System.debug() statements and curly brackets when they appear alone on a line. Otherwise, multiple executable lines appearing on single line are calculated as one line. Single executable statement appearing on multiple lines is counted as multiple lines.
Some best practices that you should adhere to are as follows:
- Test for good data, bad data and bulk data. Your test classes should have test methods for testing both the desired situation and undesired situations and they should check it for multiple records.
- If there are multiple tests that are going to run in a single transaction, remember that all those tests would be consuming single set of governor limits. This could lead to some tests failing due to exhaustion of governor limits. Ensure that each test method gets its own fresh set of governor limits by making use of startTest() and stopTest() methods of the Test class.
- To ensure users see only data that they are supposed to see when the apex code is executed in the background, you should test with Sharing. You can achieve this by using the System.runAs() method.
- If you are using static resources in your tests, ensure that those resources are also available on the production org so that your tests don’t fail due to unavailability of the resource.
- Don’t hardcode IDs in your tests, since IDs are unique across orgs.
- Have only one test class per Apex class you are testing.
Of course there are numerous general testing best practices that also apply to apex tests. You should try following all of those. The above write-up does mention but a few that are specific to testing in Salesforce.