Write Tests for an Extension Provider
Writing tests for an extension provider is an essential step in the development process. These tests ensure that the functionality of the extension class is working as intended and that any changes made to the class do not break existing functionality. It is a best practice to write tests before writing the actual code to have a clear understanding of the requirements and to have a reliable set of tests in place to verify that the code is working correctly.
In this guide, we describe the process of creating tests for extension providers.
1. Testing and Code Coverage
Testing code coverage is a measure of how much of the Apex code in your org has been executed by your test methods. You are required to have at least 75% code coverage for your Apex classes and triggers. When you run your test methods, Salesforce calculates the code coverage and displays it in the Developer Console and the Apex Test Execution page.
Refer to Testing and Code Coverage for more information.
Consider the following base Apex class and extension provider class.
Base Apex Class
1global virtual class CommerceDX_Inventory {
2 global virtual Integer calculateInventory(String webstoreName, String eventName){
3 return 10;
4 }
5}Extension Provider Class
1public class Custom_Inventory extends CommerceDX_Inventory {
2 public override Integer calculateInventory(String webstoreName, String eventName) {
3 return super.calculateInventory(webstoreName, eventName);
4 }
5}Test Apex Class
1@isTest
2public class Custom_InventoryTest {
3 @isTest
4 static void testCalculateInventory() {
5 String webstoreName = 'myWebstore';
6 String eventName = 'dreamforce';
7 Integer inventory = new Custom_Inventory().calculateInventory(webstoreName, eventName);
8 System.assertEquals(10, inventory, 'Correct inventory calculation for non-blank webstoreName and eventName');
9 }
10}2. Test with the Database
When testing an Apex class that interacts with the database, you can use test data to set up the test context and assert that the class is behaving as expected. Here’s an example of an Apex test class that tests an Apex class that interacts with the database.
Extension Provider Class
1public class Custom_InventoryDB extends CommerceDX_Inventory{
2 public override Integer calculateInventory(String webstoreName, String eventName) {
3 Integer inventory = super.calculateInventory(webstoreName, eventName);
4 List<Product2> product = [SELECT ProductCode FROM Product2];
5 System.debug(product);
6 if(product.size() > 0){
7 return inventory;
8 }
9 return -1;
10 }
11}Test Apex Class
1@isTest
2public class Custom_InventoryDBTest {
3 @isTest
4 static void testCalculateInventory() {
5 String eventName = 'dreamforce';
6 String webstoreName = 'myWebstore';
7
8 // Verify NO DATA before testing
9 List<Product2> product = [SELECT ProductCode FROM Product2 WHERE Name =:eventName];
10 System.assertEquals(0, product.size());
11 Integer inventory = new Custom_InventoryDB().calculateInventory(webstoreName, eventName);
12 System.assertEquals(-1, inventory);
13
14 // Create test data
15 Product2 testProduct = new Product2(Name ='dreamforce');
16 insert testProduct;
17
18 // Verify result after insterting data
19 inventory = new Custom_InventoryDB().calculateInventory(webstoreName, eventName);
20 product = [SELECT ProductCode FROM Product2 WHERE Name =:eventName];
21 System.assertEquals(1, product.size());
22 System.assertEquals(10, inventory);
23 }
24}To verify the test data is not persistent, we can create another test like the following.
1@isTest
2static void verifyTestDataAreNotPersistent() {
3 List<Product2> product = [SELECT ProductCode FROM Product2 WHERE Name =:eventName];
4 System.assertEquals(0, product.size());
5 Integer inventory = new Custom_InventoryDB().calculateInventory(webstoreName, eventName);
6 System.assertEquals(-1, inventory);
7}Refer to the Understanding Test Data for more information.
3. Mock HTTP callout
Mocking HTTP callout is a feature in Apex that allows you to simulate an HTTP callout in a test method, without actually calling an external service. Mocking the callout is useful for testing Apex code that makes HTTP callouts because it allows you to test the code in isolation, without depending on the availability or behavior of the external service.
Extension Provider Class
1public class Custom_InventoryCallOut extends CommerceDX_Inventory {
2 public override Integer calculateInventory(String webstoreName, String eventName) {
3 Integer inventory = super.calculateInventory(webstoreName, eventName);
4 HttpResponse res = makeCallout();
5 if(res.getStatusCode() == 200){
6 return 2 * inventory;
7 }
8 return inventory;
9 }
10 private HttpResponse makeCallOut() {
11 HttpRequest req = new HttpRequest();
12 req.setEndpoint('https://example.com/example/test');
13 req.setMethod('GET');
14 Http h = new Http();
15 HttpResponse res = h.send(req);
16 return res;
17 }
18}HttpCalloutMock Implementation
1public class myCallOutMock implements HttpCalloutMock {
2 public HttpResponse respond(HttpRequest req) {
3 HttpResponse res = new HttpResponse();
4 res.setBody('{ "message": "Hello from the mock callout!" }');
5 res.setHeader('Content-Type', 'application/json');
6 res.setStatusCode(200);
7 return res;
8 }
9}Test Apex Class
1@isTest
2public class Custom_InventoryCallOutTest {
3 @isTest
4 static void testMakeCallout() {
5 String eventName = 'dreamforce';
6 String webstoreName = 'myWebstore';
7 Test.setMock(HttpCalloutMock.class, new myCalloutMock());
8 Integer inventory = new Custom_InventoryCallOut().calculateInventory(webstoreName,eventName);
9 System.assertEquals(20, inventory);
10 }
11}Refer to Testing HTTP Callouts by Implementing the HttpCalloutMock Interface for more information.
4. Mock file-based Apex
Refer to Mock the Base Apex Class for more information.
5. Observing Governor Limit Metrics for a Test
Governor limits are a set of limits enforced by Salesforce to ensure that Apex code doesn’t consume too many resources. In an Apex test class, you can use the Test.startTest() and Test.stopTest() methods to set an isolated environment between them so that you can see how your code behaves under different conditions. The Test.startTest() and Test.stopTest() methods also reset side governor limits between them, which are independent of the limits outside of it. Here’s an example of how you can observe governor limits in the test:
Extension Provider Class
1public class Custom_InventoryLimit extends CommerceDX_Inventory{
2 public override Integer calculateInventory(String webstoreName, String eventName) {
3 return [SELECT count() FROM Product2 WHERE Name =:eventName];
4 }
5}Test Apex Class
1@isTest
2public class Custom_InventoryLimitsTest {
3 @isTest
4 static void testGovernorLimits() {
5 // Test env starts
6 Test.startTest();
7
8 // Code that you want to test goes here
9 String webstoreName = 'myWebstore';
10 String eventName = 'dreamforce';
11
12 // Query once through the extension provider class
13 Integer count = new Custom_InventoryLimit().calculateInventory(webstoreName, eventName);
14 System.assertEquals(Limits.getQueries(), 1);
15
16 // Test env ends
17 Test.stopTest();
18
19 // verify governor limits reset to 0 out side of Test suites
20 System.assertEquals(Limits.getQueries(), 0);
21 }
22}In this example, the test class starts the test environment using the Test.startTest() method and the code that you want to test goes in between the start and stop test methods. Then, the test class stops the test environment using the Test.stopTest() method.
Refer to Using Limits, startTest, and stopTest for more information.
After that, the test class checks the governor limits. The Limits.getQueries() method returns the number of SOQL queries executed within the test method’s pairs. You can use other methods to check governor limits, including Limits.getCpuTime() and Limits.getLimitContexts().
Refer to Apex Governor Limits for more information.
5.1 Read-only Governor Limits
A read-only class is a class that only performs read operations, such as queries, but does not modify the data. To guarantee that your extension provider class is executed in read-only mode, write a test that checks the DML operation for your Apex class.
Extension Provider Class
1public class Custom_InventoryReadOnly extends CommerceDX_Inventory {
2 public override Integer calculateInventory(String webstoreName, String eventName) {
3 Product2 testProduct = new Product2(Name ='dreamforce');
4 insert testProduct;
5 return 0;
6 }
7}Apex Class Test
1@isTest
2public class Custom_InventoryReadOnlyLimitsTest {
3 @isTest
4 private static void testSOQLQueriesLimitWithReadOnlyClass() {
5 String webstoreName = 'myWebstore';
6 String eventName = 'TestProduct';
7 Integer result = new Custom_InventoryReadOnly().calculateInventory(webstoreName, eventName);
8 // Assertion to make sure you don't accidentally write a DML operation in your Apex class!
9 System.assertEquals(0,Limits.getDmlStatements(), 'Extension provider class should be read only');
10 }
11}Next Steps
Now you that you have written your test code, you can run it on your extension provider class and make any necessary changes.
Refer to Exception Statements for more information.