Accessing Private Test Class Members

Test methods are defined in a test class, separate from the class they test. This can present a problem when having to access a private class member variable from the test method, or when calling a private method. Because these are private, they aren’t visible to the test class. You can either modify the code in your class to expose public methods that will make use of these private class members, or you can simply annotate these private class members with @TestVisible. When you annotate private or protected members with this annotation, they can be accessed by test methods and only code running in test context.

This example shows how @TestVisible is used with private member variables, a private inner class with a constructor, a private method, and a private custom exception. All these can be accessed in the test class because they’re annotated with @TestVisible. The class is listed first and is followed by a test class containing the test methods.

1public with sharing class VisibleSampleClass {
2    // Private member variables
3    @TestVisible private Integer recordNumber = 0;
4    @TestVisible private String areaCode = '(415)';
5    // Public member variable
6    public Integer maxRecords = 1000;
7    
8    // Private inner class
9    @TestVisible with sharing class Employee {
10        String fullName;
11        String phone;
12        
13        // Constructor
14        @TestVisible Employee(String s, String ph) {
15            fullName = s;
16            phone = ph;
17        }
18    }
19       
20    // Private method
21    @TestVisible private String privateMethod(Employee e) {
22        System.debug('I am private.');
23        recordNumber++;
24        String phone = areaCode + ' ' + e.phone;
25        String s = e.fullName + '\'s phone number is ' + phone;
26        System.debug(s);
27        return s;
28    }
29    
30    // Public method
31    public void publicMethod() {
32        maxRecords++;
33        System.debug('I am public.');    
34    }
35    
36    // Private custom exception class
37    @TestVisible private with sharing class MyException extends Exception {}
38}
1// Test class for VisibleSampleClass
2@IsTest
3private with sharing class VisibleSampleClassTest {
4
5    // This test method can access private members of another class 
6    // that are annotated with @TestVisible.
7    @IsTest
8    static void test1() {
9        VisibleSampleClass sample = new VisibleSampleClass ();
10
11        // Access private data members and update their values
12        sample.recordNumber = 100;
13        sample.areaCode = '(510)';
14        
15        // Access private inner class
16        VisibleSampleClass.Employee emp = 
17            new VisibleSampleClass.Employee('Joe Smith', '555-1212');
18        
19        // Call private method
20        String s = sample.privateMethod(emp);
21        
22        // Verify result
23        Assert.isTrue(
24            s.contains('(510)') &&
25            s.contains('Joe Smith') &&
26            s.contains('555-1212'));
27    }
28    
29    // This test method can throw private exception defined in another class
30    @IsTest
31    static void test2() {
32        // Throw private exception.
33        try {
34            throw new VisibleSampleClass.MyException('Thrown from a test.');
35        } catch(VisibleSampleClass.MyException e) {
36            // Handle exception 
37        }
38    }
39    
40    @IsTest
41    static void test3() {
42        // Access public method.
43        // No @TestVisible is used.
44        VisibleSampleClass sample = new VisibleSampleClass ();
45        sample.publicMethod();
46    }   
47
48}

The @TestVisible annotation can be handy when you upgrade the Salesforce API version of existing classes containing mixed test and non-test code. Because test methods aren’t allowed in non-test classes starting in API version 28.0, you must move the test methods from the old class into a new test class (a class annotated with @IsTest) when you upgrade the API version of your class. You might run into visibility issues when accessing private methods or member variables of the original class. In this case, just annotate these private members with @TestVisible.