Monday, December 19, 2022

Salesforce Apex Test Class with an Example, Annotations, Methods & Best Practices of Test Class

Introduction:

Testing(writing Unit tests) is an important part of the Salesforce Development Life Cycle. Before deploying the code developed into the production environment, Salesforce requires at least 75% of the code to be covered by our test classes. Salesforce has done that to make sure that our code doesn’t break in any situation in Production.

  • Unit tests are the developers’ tests to ensure that functionality is working as expected, considering Both positive & Negative tests.
  • Unit tests are also used in long-term development projects to ensure that project is running error-free & smooth.

Advantage of Unit Testing/Test Classes:

  • Ensuring that your Apex classes and triggers work as expected
  • Having a suite of regression tests that can be rerun every time classes and triggers are updated to ensure that future updates you make to your app don’t break existing functionality
  • Meeting the code coverage requirements for deploying Apex to production or distributing Apex to customers via packages.
  • High-quality apps delivered to the production org, which makes production users more productive
  • High-quality apps are delivered to package subscribers, which increases your customer’s trust.
  • Reduce the bug Cost
  • Perform Bulk tests

Annotations:

  • @isTest – used at the Class or Method level to indicate it only contains code that supports Test Coverage. Classes defined with this annotation do not count against your organization limit for Apex code.
  • @testSetup – used to indicate a method is specifically used to set up test data. When supplied, the @testSetup method is executed before any other methods in the test class. Each test method will have access to the original set of constructed test data regardless of how any other test method uses that data.
  • @testVisible –When constructing Apex logic, it makes sense to define members such as methods, variables, and inner classes as private or protected. Doing so, however, can make achieving Test Coverage more challenging. Fortunately, Salesforce has thought ahead and provided us with the @testVisible annotation.  Using this annotation on private or protected members allows test class access but still preserves the defined visibility to non-test classes.
  • @isTest(SeeAllData=True) –Ideally, your Salesforce test classes should be responsible for creating their own data. However, sometimes you need access to existing data, and using @isTest(SeeAllData=True) allows your test classes and test methods this access. Available as of API version 24.0, there are some caveats when using. “SeeAllData=True” can be used at the Class and Method level. When using “SeeAllData=True” at the Class level, all methods get access to existing data but using “SeeAllData=True” at just the Method level only allows those Methods access to existing data. Lastly, using “SeeAllData=False” at the Method level will not override “SeeAllData=True” used at the Class level.
  • @isTest(isParallel=true) — Use the @isTest(isParallel=true) annotation to indicate test classes that can run in parallel. Default limits on the number of concurrent tests do not apply to these test classes.

Methods used in Salesforce Test Classes:

Test.startTest() and Test.stopTest()

StartTest()  – Marks the point in your test code when your test actually begins. Use this method when you are testing governor limits.

public static Void startTest()

StopTest()  – Marks the point in your test code when your test ends. Use this method in conjunction with the startTest method.

public static Void stopTest()

  • The startTest method marks the point in your test code when your test actually begins. Each test method is allowed to call this method only once
  • All of the code before this method should be used to initialize variables, populate data structures, and so on, allowing you to set up everything you need to run your test. 
  • Any code that executes after the call to startTest and before stopTest is assigned a new set of governor limits.
  • The startTest method does not refresh the test context: it adds a context to your test. For example, if your class makes 98 SOQL queries before it calls startTest. The first significant statement after startTest is a DML statement. The program can now make an additional 100 queries. Once stopTest is called, however, the program goes back into the original context and can only make 2 additional SOQL queries before reaching the limit of 100
  • All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously

System.Assert:

It accepts two parameters: First one (mandatory) is the condition test and the second one is used to display a message in case the condition fails.Syntax: System.assert(var1 == var2,”msg”)

System.AssertEquals:

It accepts three parameters; the first two (mandatory) are the variables that will be tested for equality or inequality. The third (optional)one is used to display a message in case the condition fails.Syntax: System.assertEquals(var1, var2,”msg”);

System.runAs()

All Apex code runs in system mode, where the permissions and record sharing of the current user are not taken into account. The system method runAs enables us to write test methods that change the user context to an existing user or a new user so that the user’s record sharing is enforced. The runAs method doesn’t enforce user permissions or field-level permissions, only record sharing. The following items use the permissions granted by the user-specified with runAsrunning as a specific user: Dynamic Apex Methods using with sharing or without sharing

Shared records
The original permissions are reset after runAs completes.The runAs method ignores user license limits.

Test.isRunningTest()

The Test.isRunningTest()  method is used to identify if the piece of code being executed is invoked from a Test class execution or other artefacts such as a Trigger, Batch Job etc. Returns true if the code being executed is invoked from a test class otherwise, returns a false.
Example:
Performing web service callouts in Apex are not supported within Test Code. Hence we could use the Test.isRunningTest() to conditionally identify and route the execution of a code block that calls the Test Mock framework to simulate mock, callout response.

Test.loadData()

Using the Test.loadData method, you can populate data in your test methods without writing any code lines. Follow these steps:

  1. Add the data in a .csv file.
  2. Create a static resource for this file.
  3. Call Test.loadData within your test method and passing it the sObject type token and the static resource name.

For example, for Account records and a static resource name of myResource, make the following call:

List<sObject> ls = Test.loadData(Account.sObjectType, 'myResource');

The Test.loadData method returns a list of sObjects that correspond to each record inserted.

You must create the static resource before calling this method. The static resource is a comma-delimited file ending with a .csv extension. The file contains field names and values for the test records. The first line of the file must contain the field names, and subsequent lines are the field values. To learn more about static resources, see “Defining Static Resources” in the Salesforce online help. Once you create a static resource for your .csv file, the static resource will be assigned a MIME type. Supported MIME types are:

  • text/csv
  • application/vnd.ms-excel
  • application/octet-stream
  • text/plain

Unit Tests to be Done for?

we have to write test classes for below items/components in salesforce.

  1. Apex Trigger
  2. Apex Class – Handler/Helper, WebService, Apex REST, SOAP
  3. VF Page
  4. Apex Batch/Queueable/Future Method
  5. Custom Controller

Where to Write Test Class

You can use any of below tools to write test classes

  1. Developer console
  2. Salesforce UI , setup -> apex classes ->create/open existing test class
  3. Visual studio code
  4. Any IDE tool.

Sample Test Class:

Apex Trigger

trigger SampleTrigger on Car__c (before insert) {
Car__c[] Cars = Trigger.new;
SampleApex.applyDiscount(Cars);
}

Apex Class:

public class SampleApex{
public static void applyDiscount(Car__c[] Cars) {
for(Car__c c :Cars){
c.Price__c *= 0.9;
}
}
}

Apex Test Class:

@isTest
private class SampleApexTest{
static testMethod void validateSampleApex() {
Car__c c = new Car__c(Name='BMW', Price__c=100000);
 
Test.startTest();
insert c;
Test.stopTest();
 
// Retrieve the record
c = [SELECT Price__c FROM Car__c WHERE Id =:c.Id];
 
// Test that the trigger correctly updated the price
System.assertEquals(90000, c.Price__c);
}
}

Best Practices & Notes of Salesforce Test Class:

1. Test class must start with @isTest annotation if the class version is more than 25
2. Test environment support @testVisible, @testSetUp as well
3. Unit test is to test a particular piece of code working properly or not.
4. Unit test method takes no argument, commits no data to database, sends no email, flagged with testMethod keyword.
5. To deploy to production, at-least 75% code coverage is required
6. System. Debug statements are not counted as a part of the apex code limit.
7. Test method and test classes are not counted as a part of code limit
9. We should not focus on the percentage of code coverage. We should make sure that every use case should be covered, including positive, negative, bulk, and single record.
Single Action -To verify that the single record produces the correct and expected result.
Bulk action -Any apex record trigger, class, or extension must be invoked for 1-200 records.
Positive behavior: Test every expected behavior that occurs through every expected permutation. The user filled out every correct data and did not go past the limit.
Negative Testcase:-Not to add future date, Not to specify a negative amount.
Restricted User:-Test whether a user with restricted access is used in your code.
10. Test class should be annotated with @isTest.
11. @isTest annotation with test method is equivalent to testMethod keyword.
12. Test method should static and no void return type.
13. Test class and method default access are private, no matter to add access specifier.
14. classes with @isTest annotation can’t be an interface or enum.
15. Test method code can’t be invoked by non-test request.
16. Stating with salesforce API 28.0 test method can not reside inside non-test classes.
17. @Testvisible annotation to make visible private methods inside test classes.
18. Test method can not be used to test web-service call out. Please use call-out mock.
19. You can’t send emails from the test method.
20.User, profile, organization, AsyncApexjob, Corntrigger, RecordType, ApexClass, ApexComponent ,ApexPage we can access without (seeAllData=true) .
21. SeeAllData=true will not work for API 23 version eailer .
22. Accessing static resource test records in test class e,g List<Account> accList=Test.loadData(Account,SobjectType,’ResourceName’).
23. Create TestFactory class with @isTest annotation to exclude from organization code size limit.
24. @testSetup creates test records once in a method and is used in every test class test method.
25. We can run unit tests by using Salesforce Standard UI,Force.com IDE, Console, API.
26. The Maximum number of test classes run per 24 hours the period is not greater than 500 or 10 multiplication of test classes of your organization.
27. As apex runs in system mode, the permission and record sharing are not taken into account. So we need to use system.runAs to enforce record sharing.
28. System.runAs will not enforce user permission or field level permission.
29. Every test to runAs count against the total number of DML issued in the process

No comments:

Post a Comment