Apex Testing, Debugging, Deployment Flashcards
Apex testing coverage
Cover as many lines of code as possible. Before you can deploy Apex or package it for AppExchange, the following must be true.
- Unit tests must cover at least 75% of your Apex code, and all of those tests must complete successfully.
- Every trigger must have some test coverage.
- All classes and triggers must compile successfully.
- When deploying Apex to a production organization, each unit test in your organization namespace is executed by default.
- Calls to System.debug aren’t counted as part of Apex code coverage.
- Test methods and test classes aren’t counted as part of Apex code coverage.
- While only 75% of your Apex code must be covered by tests, don’t focus on the percentage of code that is covered. Instead, make sure that every use case of your application is covered, including positive and negative cases, as well as bulk and single records. This approach ensures that 75% or more of your code is covered by unit tests.
Tests don’t run in parallel in metadata deployments, package installations, or change set deployments.
What to Test in Apex
Single action
Bulk actions
Positive behavior
Negative behavior
Restricted user
What Are Apex Unit Tests?
Unit test methods take no arguments, commit no data to the database, and send no emails. Such methods are flagged with the @IsTest annotation in the method definition. Unit test methods must be defined in test classes, that is, classes annotated with @IsTest.
The @IsTest annotation on methods is equivalent to the testMethod keyword. As best practice, Salesforce recommends that you use @IsTest rather than testMethod. The testMethod keyword may be versioned out in a future release.
Classes and methods defined as @IsTest can be either private or public. The access level of test classes methods doesn’t matter. You need not add an access modifier when defining a test class or test methods. The default access level in Apex is private. The testing framework can always find the test methods and execute them, regardless of their access level.
Classes defined as @IsTest must be top-level classes and can’t be interfaces or enums.
Methods of a test class can only be called from a test method or code invoked by a test method; non-test requests can’t invoke it.
Unit Test Considerations
Here are some things to note about unit tests.
- Starting with Salesforce API 28.0, test methods can no longer reside in non-test classes and must be part of classes annotated with IsTest.
- Test methods can’t be used to test Web service callouts. Instead, use mock callouts.
- You can’t send email messages from a test method.
- Since test methods don’t commit data created in the test, you don’t have to delete test data upon completion.
- If the value of a static member variable in a test class is changed in a testSetup or test method, the new value isn’t preserved. Other test methods in this class get the original value of the static member variable. This behavior also applies when the static member variable is defined in another class and accessed in test methods.
- Tracked changes for a record (FeedTrackedChange records) in Chatter feeds aren’t available when test methods modify the associated record.
- Since test methods don’t commit data, they don’t result in the creation of FeedTrackedChange records. Similarly, field history tracking records can’t be created in test methods because they require other sObject records to be committed first. For example, AccountHistory records can’t be created in test methods because Account records must be committed first.
- If your tests include DML, make sure that you don’t exceed the MAX_DML_ROWS limit.
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.
Understanding Test Data
Apex test data is transient and isn’t committed to the database.
This means that after a test method finishes execution, the data inserted by the test doesn’t persist in the database. As a result, there is no need to delete any test data at the conclusion of a test. Likewise, all the changes to existing records, such as updates or deletions, don’t persist. This transient behavior of test data makes the management of data easier as you don’t have to perform any test data cleanup. At the same time, if your tests access organization data, this prevents accidental deletions or modifications to existing records.
By default, existing organization data isn’t visible to test methods, with the exception of certain setup objects. You should create test data for your test methods whenever possible. However, test code saved against Salesforce API version 23.0 or earlier has access to all data in the organization.
Isolation of Test Data from Organization Data in Unit Tests
By default, Apex test methods (API version 24.0 and later) can’t access pre-existing org data such as standard objects, custom objects, and custom settings data. They can only access data that they create. However, objects that are used to manage your organization or metadata objects can still be accessed in your tests. These are some examples of such objects.
User
Profile
Organization
CronTrigger
RecordType
ApexClass
ApexTrigger
ApexComponent
ApexPage
Whenever possible, create test data for each test. You can disable this restriction by annotating your test class or test method with the IsTest(SeeAllData=true) annotation.
Test code saved using Salesforce API version 23.0 or earlier continues to have access to all data in the organization and its data access is unchanged.
Data Access Considerations
When working with data silo Apex tests(A data silo test is a test method that doesn’t have access to org data.), cross-object field references using the Owner relationship aren’t supported. Due to this limitation, SELECT Owner.IsActive FROM Account returns null when run within a data silo Apex test.
If a new test method saved using Salesforce API version 24.0 or later calls a method in another class saved using version 23.0 or earlier, the data access restrictions of the caller are enforced in the called method. The called method can’t access organization data because the caller can’t access it, even though it was saved in an earlier version.
The IsTest(SeeAllData=true) annotation has no effect when added to Apex code saved using Salesforce API version 23.0 and earlier.
This access restriction to test data applies to all code running in test context. For example, if a test method causes a trigger to execute and the test can’t access organization data, the trigger won’t be able to either.
If a test makes a Visualforce request, the executing test stays in test context but runs in a different thread. Therefore, test data isolation is no longer enforced. In this case, the test will be able to access all data in the organization after initiating the Visualforce request. However, if the Visualforce request performs a callback, such as a JavaScript remoting call, any data inserted by the callback isn’t visible to the test.
The VLOOKUP validation rule function, in API version 27.0 and earlier, always looks up org data in addition to test data when fired by a running Apex test. Starting with version 28.0, the VLOOKUP validation rule function no longer accesses organization data from a running Apex test. The function looks up only data created by the test, unless the test class or method is annotated with IsTest(SeeAllData=true).
There can be some cases where you can’t create certain types of data from your test method because of specific limitations. Here are some examples of such limitations.
Some standard objects aren’t creatable.
For some sObjects that have fields with unique constraints, inserting duplicate sObject records results in an error. For example, inserting CollaborationGroup sObjects with the same names results in an error because CollaborationGroup records must have unique names. This error occurs whether your test is annotated with IsTest(SeeAllData=true), or not.
Records that are created only after related records are committed to the database, like tracked changes in Chatter. Tracked changes for a record (FeedTrackedChange records) in Chatter feeds aren’t available when test methods modify the associated record. FeedTrackedChange records require the change to the parent record they’re associated with to be committed to the database before they’re created. Since test methods don’t commit data, they don’t result in the creation of FeedTrackedChange records. Similarly, field history tracking records can’t be created in test methods because they require other sObject records to be committed first. For example, AccountHistory records can’t be created in test methods because Account records must be committed first.
Using the isTest(SeeAllData=True) Annotation
Annotate your test class or test method with IsTest(SeeAllData=true) to open up data access to records in your organization. The IsTest(SeeAllData=true) annotation applies to data queries but doesn’t apply to record creation or changes, including deletions. New and changed records are still rolled back in Apex tests even when using the annotation.
Warning:
By annotating your class with @isTest(SeeAllData=true), you allow test methods to access all org records. The best practice, however, is to run Apex tests with data silo using @isTest(SeeAllData=false). Depending on the API version you’re using, the default annotation can vary.
Considerations for the @IsTest(SeeAllData=true) Annotation
If a test class is defined with the @IsTest(SeeAllData=true) annotation, the SeeAllData=true applies to all test methods that don’t explicitly set the SeeAllData keyword.
The @IsTest(SeeAllData=true) annotation is used to open up data access when applied at the class or method level. However, if the containing class has been annotated with @IsTest(SeeAllData=true), annotating a method with @IsTest(SeeAllData=false) is ignored for that method. In this case, that method still has access to all the data in the organization. Annotating a method with @IsTest(SeeAllData=true) overrides, for that method, an @IsTest(SeeAllData=false) annotation on the class.
@IsTest(SeeAllData=true) and @IsTest(IsParallel=true) annotations can’t be used together on the same Apex method.
Loading Test Data
Using the Test.loadData method, you can populate data in your test methods without having to write many lines of code.
Follow these steps:
Add the data in a .csv file.
Create a static resource for this file.
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.</sObject>
You must create the static resource prior to 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.
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
Test.loadData Example
The following are steps for creating a sample .csv file and a static resource, and calling Test.loadData to insert the test records.
Create a .csv file that has the data for the test records. This sample .csv file has three account records. You can use this sample content to create your .csv file.
Name,Website,Phone,BillingStreet,BillingCity,BillingState,BillingPostalCode,BillingCountry
sForceTest1,http://www.sforcetest1.com,(415) 901-7000,The Landmark @ One Market,San Francisco,CA,94105,US
sForceTest2,http://www.sforcetest2.com,(415) 901-7000,The Landmark @ One Market Suite 300,San Francisco,CA,94105,US
sForceTest3,http://www.sforcetest3.com,(415) 901-7000,1 Market St,San Francisco,CA,94105,US
Create a static resource for the .csv file:
From Setup, enter Static Resources in the Quick Find box, then select Static Resources.
Click New.
Name your static resource testAccounts.
Choose the file you created.
Click Save.
Call Test.loadData in a test method to populate the test accounts.
@isTest
private class DataUtil {
static testmethod void testLoadData() {
// Load the test accounts from the static resource
List<sObject> ls = Test.loadData(Account.sObjectType, 'testAccounts');
// Verify that all 3 test accounts were created
System.assert(ls.size() == 3);</sObject>
// Get first test account Account a1 = (Account)ls[0]; String acctName = a1.Name; System.debug(acctName); // Perform some testing using the test records } }
Common Test Utility Classes for Test Data Creation
Common test utility classes are public test classes that contain reusable code for test data creation.
Public test utility classes are defined with the IsTest annotation, and as such, are excluded from the organization code size limit and execute in test context. They can be called by test methods but not by non-test code.
The methods in the public test utility class are defined the same way methods are in non-test classes. They can take parameters and can return a value. The methods must be declared as public or global to be visible to other test classes. These common methods can be called by any test method in your Apex classes to set up test data before running the test. While you can create public methods for test data creation in a regular Apex class, without the IsTest annotation, you don’t get the benefit of excluding this code from the organization code size limit.
Using Test Setup Methods
Use test setup methods (methods that are annotated with @testSetup) to create test records once and then access them in every test method in the test class. Test setup methods can be time-saving when you need to create reference or prerequisite data for all test methods, or a common set of records that all test methods operate on.
Test setup methods can reduce test execution times especially when you’re working with many records. Test setup methods enable you to create common test data easily and efficiently. By setting up records once for the class, you don’t need to re-create records for each test method. Also, because the rollback of records that are created during test setup happens at the end of the execution of the entire class, the number of records that are rolled back is reduced. As a result, system resources are used more efficiently compared to creating those records and having them rolled back for each test method.
If a test class contains a test setup method, the testing framework executes the test setup method first, before any test method in the class. Records that are created in a test setup method are available to all test methods in the test class and are rolled back at the end of test class execution. If a test method changes those records, such as record field updates or record deletions, those changes are rolled back after each test method finishes execution. The next executing test method gets access to the original unmodified state of those records.
Syntax
Test setup methods are defined in a test class, take no arguments, and return no value. The following is the syntax of a test setup method.
@testSetup static void methodName() {
}
Test Setup Method Considerations
* Test setup methods are supported only with the default data isolation mode for a test class. If the test class or a test method has access to organization data by using the @isTest(SeeAllData=true) annotation, test setup methods aren’t supported in this class. Because data isolation for tests is available for API versions 24.0 and later, test setup methods are also available for those versions only.
* You can have only one test setup method per test class.
* If a fatal error occurs during the execution of a test setup method, such as an exception that’s caused by a DML operation or an assertion failure, the entire test class fails, and no further tests in the class are executed.
* If a test setup method calls a non-test method of another class, no code coverage is calculated for the non-test method.
Run Unit Test Methods
To verify the functionality of your Apex code, execute unit tests. You can run Apex test methods in the Developer Console, in Setup, in the Salesforce extensions for Visual Studio Code, or using the API.
You can run these groupings of unit tests.
* Some or all methods in a specific class
* Some or all methods in a set of classes
* A predefined suite of classes, known as a test suite
* All unit tests in your org
*
To run a test, use any of the following:
* Salesforce user interface
* Salesforce extensions for Visual Studio Code and Code Builder
* Developer Console
* The API
All Apex tests that are started from the Salesforce user interface (including the Developer Console) run asynchronously and in parallel. Apex test classes are placed in the Apex job queue for execution. The maximum number of test classes that you can run per 24-hour period is the greater of 500 or 10 multiplied by the number of test classes in the org. For sandbox and Developer Edition organizations, this limit is higher and is the greater of 500 or 20 multiplied by the number of test classes in the org.
Apex tests that run as part of a deployment always run synchronously and serially.
Running Tests Through the Salesforce User Interface
You can run unit tests on the Apex Test Execution page. Tests started on this page run asynchronously, that is, you don’t have to wait for a test class execution to finish. The Apex Test Execution page refreshes the status of a test and displays the results after the test completes.
From Setup, enter Apex Test Execution in the Quick Find box, then select Apex Test Execution.
Click Select Tests….
Select the tests to run. The list of tests includes only classes that contain test methods.
Note
If you have Apex classes that are installed from a managed package, you must compile these classes first by clicking Compile all classes on the Apex Classes page so that they appear in the list.
To select tests from an installed managed package, select the managed package’s corresponding namespace from the dropdown list. Only the classes of the managed package with the selected namespace appear in the list.
To select tests that exist locally in your organization, select [My Namespace] from the dropdown list. Only local classes that aren’t from managed packages appear in the list.
To select any test, select [All Namespaces] from the dropdown list. All the classes in the organization appear, even those in a managed package.
Note
Classes with tests currently running don’t appear in the list.
To opt out of collecting code coverage information during test runs, select Skip Code Coverage.
Click Run.
After you run tests using the Apex Test Execution page, you can view code coverage details in the Developer Console.
From Setup, enter Apex in the Quick Find box, select Apex Test Execution, then click View Test History to view all test results for your organization, not just tests that you have run. Test results are retained for 30 days after they finish running, unless cleared.
Running Tests Using the Developer Console
In the Developer Console, you can execute some or all tests in specific test classes, set up and run test suites, or run all tests. The Developer Console runs tests asynchronously in the background, unless your test run includes only one class and you’ve not chosen Always Run Asynchronously in the Test menu. Running tests asynchronously lets you work in other areas of the Developer Console while tests are running. After the tests execute, you can inspect the test results in the Developer Console. Also, you can inspect the overall code coverage for classes covered by the tests.
Running Tests Using the API
You can use the runTests() call from SOAP API to run tests synchronously.
RunTestsResult[] runTests(RunTestsRequest ri)
This call allows you to run the following, as specified in the RunTestsRequest object:
All tests in all classes
All tests in a specific namespace
All tests in a subset of classes in a specific namespace
It returns the following:
Total number of tests that ran
Code coverage statistics
Error information for each failed test
Information for each test that succeeds
Time it took to run the test
For more information on runTests(), see runTests() in the SOAP API Developer Guide.
You can also run tests using the Tooling REST API. Use the /runTestsAsynchronous/ and /runTestsSynchronous/ endpoints to run tests asynchronously or synchronously. For usage details, see Tooling API: REST Resources.
Running Tests Using ApexTestQueueItem
You can run tests asynchronously using ApexTestQueueItem and ApexTestResult. These objects let you add tests to the Apex job queue and check the results of the completed test runs. This process enables you to not only start tests asynchronously but also schedule your tests to execute at specific times by using the Apex scheduler. See Apex Scheduler for more information.
Insert an ApexTestQueueItem object to place its corresponding Apex class in the Apex job queue for execution. The Apex job executes the test methods in the class. After the job executes, ApexTestResult contains the result for each single test method executed as part of the test.
ApexTestResult rows are also generated for Apex tests run with the @testSetup annotation. The IsTestSetup field is set to true for these annotated tests to distinguish them from other test methods. The TestSetupTime field on ApexTestRunResult tracks the cumulative time of all setup methods for the given ApexTestRunResult.
To abort a class that is in the Apex job queue, perform an update operation on the ApexTestQueueItem object and set its Status field to Aborted.
If you insert multiple Apex test queue items in a single bulk operation, the queue items share a parent job and a test run can execute tests for several classes.
The maximum number of test queue items, and hence classes, that you can insert in the Apex job queue is the greater of 500 or 10 multiplied by the number of test classes in the org. For sandbox and Developer Edition organizations, this limit is higher and is the greater of 500 or 20 multiplied by the number of test classes in the org.
This example uses DML operations to insert and query the ApexTestQueueItem and ApexTestResult objects. The enqueueTests method inserts queue items for all classes that end with Test. It then returns the parent job ID of one queue item, which is the same for all queue items because they were inserted in bulk. The checkClassStatus method retrieves all queue items that correspond to the specified job ID. It then queries and outputs the name, job status, and pass rate for each class. The checkMethodStatus method gets information of each test method that was executed as part of the job.
// Enqueue all classes ending in “Test”.
public static ID enqueueTests() {
ApexClass[] testClasses =
[SELECT Id FROM ApexClass
WHERE Name LIKE ‘%Test’];
if (testClasses.size() > 0) {
ApexTestQueueItem[] queueItems = new List<ApexTestQueueItem>();
for (ApexClass cls : testClasses) {
queueItems.add(new ApexTestQueueItem(ApexClassId=cls.Id));
}</ApexTestQueueItem>
insert queueItems; // Get the job ID of the first queue item returned. ApexTestQueueItem item = [SELECT ParentJobId FROM ApexTestQueueItem WHERE Id=:queueItems[0].Id LIMIT 1]; return item.parentjobid; }
Using the runAs Method
Generally, all Apex code runs in system mode, where the permissions and record sharing of the current user aren’t taken into account. The system method runAs enables you 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 enforces record sharing. User permissions and field-level permissions are applied for the new context user as described in Enforcing Object and Field Permissions.
The user’s record sharing and object and field permissions are enforced within a runAs block, regardless of the sharing mode of the test class. If a user-defined method is called in the runAs block, the sharing mode enforced is that of the class where the method is defined.
You can use runAs only in test methods. The original system context is started again after all runAs test methods complete.
The runAs method ignores user license limits. You can create users with runAs even if your organization has no additional user licenses.
Every call to runAs counts against the total number of DML statements issued in the process.
In the following example, a new test user is created, then code is run as that user, with that user’s record sharing access:
@isTest
private class TestRunAs {
public static testMethod void testRunAs() {
// Setup test data
// Create a unique UserName
String uniqueUserName = ‘standarduser’ + DateTime.now().getTime() + ‘@testorg.com’;
// This code runs as the system user
Profile p = [SELECT Id FROM Profile WHERE Name=’Standard User’];
User u = new User(Alias = ‘standt’, Email=’standarduser@testorg.com’,
EmailEncodingKey=’UTF-8’, LastName=’Testing’, LanguageLocaleKey=’en_US’,
LocaleSidKey=’en_US’, ProfileId = p.Id,
TimeZoneSidKey=’America/Los_Angeles’,
UserName=uniqueUserName);
System.runAs(u) { // The following code runs as user 'u' System.debug('Current User: ' + UserInfo.getUserName()); System.debug('Current Profile: ' + UserInfo.getProfileId()); } } }
Other Uses of runAs
You can also use the runAs method to perform mixed DML operations in your test by enclosing the DML operations within the runAs block. In this way, you bypass the mixed DML error that is otherwise returned when inserting or updating setup objects together with other sObjects. See sObjects That Cannot Be Used Together in DML Operations.
There’s another overload of the runAs method (runAs(System.Version)) that takes a package version as an argument. This method causes the code of a specific version of a managed package to be used. For information on using the runAs method and specifying a package version context, see Testing Behavior in Package Versions.
Using Limits, startTest, and stopTest
The Limits methods return the specific limit for the particular governor, such as the number of calls of a method or the amount of heap size remaining.
Each method has two versions. The first version returns the amount of the resource that has been used in the current context. The second version contains the word “limit” and returns the total amount of the resource that is available for that context. For example, getCallouts returns the number of callouts to an external service that have already been processed in the current context, while getLimitCallouts returns the total number of callouts available in the given context.
In addition to the Limits methods, use the startTest and stopTest methods to validate how close the code is to reaching governor limits.
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 context of the test: it adds a context to your test. For example, if your class makes 98 SOQL queries before it calls startTest, and 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.
The stopTest method marks the point in your test code when your test ends. Use this method in conjunction with the startTest method. Each test method is allowed to call this method only once. Any code that executes after the stopTest method is assigned the original limits that were in effect before startTest was called. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously. An exception encountered during stopTest halts the synchronous processing. For example, an unhandled exception in a batch job’s execute method will prevent the finish method from running in a test context.
Adding SOSL Queries to Unit Tests
To ensure that test methods always behave in a predictable way, any Salesforce Object Search Language (SOSL) query that is added to an Apex test method returns an empty set of search results when the test method executes. If you do not want the query to return an empty list of results, you can use the Test.setFixedSearchResults system method to define a list of record IDs that are returned by the search. All SOSL queries that take place later in the test method return the list of record IDs that were specified by the Test.setFixedSearchResults method. Additionally, the test method can call Test.setFixedSearchResults multiple times to define different result sets for different SOSL queries. If you do not call the Test.setFixedSearchResults method in a test method, or if you call this method without specifying a list of record IDs, any SOSL queries that take place later in the test method return an empty list of results.
The list of record IDs specified by the Test.setFixedSearchResults method replaces the results that would normally be returned by the SOSL query if it were not subject to any WHERE or LIMIT clauses. If these clauses exist in the SOSL query, they are applied to the list of fixed search results. For example:
@isTest
private class SoslFixedResultsTest1 {
public static testMethod void testSoslFixedResults() { Id [] fixedSearchResults= new Id[1]; fixedSearchResults[0] = '001x0000003G89h'; Test.setFixedSearchResults(fixedSearchResults); List<List<SObject>> searchList = [FIND 'test' IN ALL FIELDS RETURNING Account(id, name WHERE name = 'test' LIMIT 1)]; } } Although the account record with an ID of 001x0000003G89h may not match the query string in the FIND clause ('test'), the record is passed into the RETURNING clause of the SOSL statement. If the record with ID 001x0000003G89h matches the WHERE clause filter, the record is returned. If it does not match the WHERE clause, no record is returned.
Best Practices for Parallel Test Execution
Tests that are started from the Salesforce user interface (including the Developer Console) run in parallel. Parallel test execution can speed up test run time. Sometimes, parallel test execution results in data contention issues, and you can turn off parallel execution in those cases. In particular, data contention issues and UNABLE_TO_LOCK_ROW errors can occur in the following cases:
When tests update the same records at the same time. Updating the same records typically occurs when tests don’t create their own data and turn off data isolation to access the organization’s data.
When a deadlock occurs in tests that are running in parallel and that try to create records with duplicate index field values. A deadlock occurs when two running tests are waiting for each other to roll back data. Such a wait can happen if two tests insert records with the same unique index field values.
You can prevent receiving those errors by turning off parallel test execution in the Salesforce user interface:
From Setup, enter Apex Test.
Click Options….
In the Apex Test Execution Options dialog, select Disable Parallel Apex Testing and then click OK.
Test classes annotated with IsTest(IsParallel=true) indicate that the test class can run concurrently with more than the default number of concurrent test classes. This annotation overrides default settings.
Inspecting Code Coverage
After running tests, you can view code coverage information in the Tests tab of the Developer Console. The code coverage pane includes coverage information for each Apex class and the overall coverage for all Apex code in your organization.
Also, code coverage is stored in two Lightning Platform Tooling API objects: ApexCodeCoverageAggregate and ApexCodeCoverage. ApexCodeCoverageAggregate stores the sum of covered lines for a class after checking all test methods that test it. ApexCodeCoverage stores the lines that are covered and uncovered by each individual test method. For this reason, a class can have multiple coverage results in ApexCodeCoverage—one for each test method that has tested it. You can query these objects by using SOQL and the Tooling API to retrieve coverage information. Using SOQL queries with Tooling API is an alternative way of checking code coverage and a quick way to get more details.
SELECT ApexClassOrTrigger.Name, NumLinesCovered, NumLinesUncovered
FROM ApexCodeCoverageAggregate
WHERE ApexClassOrTrigger.Name = ‘TaskUtil’
This SOQL query requires the Tooling API. You can run this query by using the Query Editor in the Developer Console and checking Use Tooling API.
SELECT ApexTestClass.Name,TestMethodName,NumLinesCovered,NumLinesUncovered
FROM ApexCodeCoverage
WHERE ApexClassOrTrigger.Name = ‘TaskUtil’
Build a Mocking Framework with the Stub API
Apex provides a stub API for implementing a mocking framework. A mocking framework has many benefits. It can streamline and improve testing and help you create faster, more reliable tests. You can use it to test classes in isolation, which is important for unit testing. Building your mocking framework with the stub API can also be beneficial because stub objects are generated at runtime. Because these objects are generated dynamically, you don’t have to package and deploy test classes. You can build your own mocking framework, or you can use one built by someone else.
You can define the behavior of stub objects, which are created at runtime as anonymous subclasses of Apex classes. The stub API comprises the System.StubProvider interface and the System.Test.createStub() method.
Let’s say we want to test the formatting method in the following class.
ublic class DateFormatter {
// Method to test
public String getFormattedDate(DateHelper helper) {
return ‘Today's date is ‘ + helper.getTodaysDate();
}
}
public class DateHelper {
// Method to stub
public String getTodaysDate() {
return Date.today().format();
}
}
DateFormatter df = new DateFormatter();
DateHelper dh = new DateHelper();
String dateStr = df.getFormattedDate(dh);
For testing, we want to isolate the getFormattedDate() method to make sure that the formatting is working properly. The return value of the getTodaysDate() method normally varies based on the day. However, in this case, we want to return a constant, predictable value to isolate our testing to the formatting. Rather than writing a “fake” version of the class, where the method returns a constant value, we create a stub version of the class. The stub object is created dynamically at runtime, and we can specify the “stubbed” behavior of its method.
To use a stub version of an Apex class:
Define the behavior of the stub class by implementing the System.StubProvider interface.
Instantiate a stub object by using the System.Test.createStub() method.
Invoke the relevant method of the stub object from within a test class.
Implement the StubProvider Interface:
@isTest
public class MockProvider implements System.StubProvider {
public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, Type returnType, List<Type> listOfParamTypes, List<String> listOfParamNames, List<Object> listOfArgs) { // The following debug statements show an example of logging // the invocation of a mocked method. // You can use the method name and return type to determine which method was called. System.debug('Name of stubbed method: ' + stubbedMethodName); System.debug('Return type of stubbed method: ' + returnType.getName()); // You can also use the parameter names and types to determine which method // was called. for (integer i =0; i < listOfParamNames.size(); i++) { System.debug('parameter name: ' + listOfParamNames.get(i)); System.debug(' parameter type: ' + listOfParamTypes.get(i).getName()); } // This shows the actual parameter values passed into the stubbed method at runtime. System.debug('number of parameters passed into the mocked call: ' + listOfArgs.size()); System.debug('parameter(s) sent into the mocked call: ' + listOfArgs); // This is a very simple mock provider that returns a hard-coded value // based on the return type of the invoked. if (returnType.getName() == 'String') return '8/8/2016'; else return null; } } StubProvider is a callback interface. It specifies a single method that requires implementing: handleMethodCall(). When a stubbed method is called, handleMethodCall() is called. You define the behavior of the stubbed class in this method. The method has the following parameters.
Instantiate a Stub Version of the Class:
public class MockUtil {
private MockUtil(){}
public static MockProvider getInstance() { return new MockProvider(); } public static Object createMock(Type typeToMock) { // Invoke the stub API and pass it our mock provider to create a // mock class of typeToMock. return Test.createStub(typeToMock, MockUtil.getInstance()); } }
Invoke the Stub Method:
@isTest
public class DateFormatterTest {
@isTest
public static void testGetFormattedDate() {
// Create a mock version of the DateHelper class.
DateHelper mockDH = (DateHelper)MockUtil.createMock(DateHelper.class);
DateFormatter df = new DateFormatter();
// Use the mocked object in the test. System.assertEquals('Today\'s date is 8/8/2016', df.getFormattedDate(mockDH)); } }
Apex Stub API Limitations
Keep the following limitations in mind when working with the Apex stub API.
- The object being mocked must be in the same namespace as the call to the Test.createStub() method. However, the implementation of the StubProvider interface can be in another namespace.
- Iterators can’t be used as return types or parameter types.
- You can’t mock the following Apex elements.
Static methods (including future methods)
Private methods
Properties (getters and setters)
Triggers
Inner classes
System types
Classes that implement the Batchable interface
Classes that have only private constructors
Debug Log
A debug log can record database operations, system processes, and errors that occur when executing a transaction or running unit tests. Debug logs can contain information about:
Database changes
HTTP callouts
Apex errors
Resources used by Apex
Automated workflow processes, such as: Workflow rules, Assignment rules, Approval processes, Validation rules
The debug log doesn’t include information from actions triggered by time-based workflows.
You can retain and manage debug logs for specific users, including yourself, and for classes and triggers. Setting class and trigger trace flags doesn’t cause logs to be generated or saved. Class and trigger trace flags override other logging levels, including logging levels set by user trace flags, but they don’t cause logging to occur. If logging is enabled when classes or triggers execute, logs are generated at the time of execution.
To view a debug log from Setup, enter Debug Logs in the Quick Find box, then select Debug Logs. Then click View next to the debug log that you want to examine. Click Download to download the log as an XML file.
Debug Log Limits
Debug logs have the following limits.
Each debug log must be 20 MB or smaller. Debug logs that are larger than 20 MB are reduced in size by removing older log lines, such as log lines for earlier System.debug statements. The log lines can be removed from any location, not just the start of the debug log.
System debug logs are retained for 24 hours. Monitoring debug logs are retained for seven days.
If you generate more than 1,000 MB of debug logs in a 15-minute window, your trace flags are disabled. We send an email to the users who last modified the trace flags, informing them that they can re-enable the trace flag in 15 minutes.
Warning
If the debug log trace flag is enabled on a frequently accessed Apex class or for a user executing requests often, the request can result in failure, regardless of the time window and the size of the debug logs.
When your org accumulates more than 1,000 MB of debug logs, we prevent users in the org from adding or editing trace flags. To add or edit trace flags so that you can generate more logs after you reach the limit, delete some debug logs.
Inspecting the Debug Log Sections
After you generate a debug log, the type and amount of information listed depends on the filter values you set for the user. However, the format for a debug log is always the same.
Session IDs are replaced with “SESSION_ID_REMOVED” in Apex debug logs
A debug log has the following sections.
Header
The header contains the following information.
The version of the API used during the transaction.
The log category and level used to generate the log. For example:
The following is an example of a header: 61.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WORKFLOW,INFO
In this example, the API version is 61.0, and the following debug log categories and levels have been set.
Apex Code DEBUG
Apex Profiling INFO
Callout INFO
Database INFO
System DEBUG
Validation INFO
Visualforce INFO
Workflow INFO
Warning
If the Apex Code log level is set to FINEST, the debug log includes details of all Apex variable assignments. Ensure that the Apex Code being traced doesn’t handle sensitive data. Before enabling FINEST log level, be sure to understand the level of sensitive data your organization’s Apex handles. Be careful with processes such as community users self-registration where user passwords can be assigned to an Apex string variable.
Execution Units
An execution unit is equivalent to a transaction. It contains everything that occurred within the transaction. EXECUTION_STARTED and EXECUTION_FINISHED delimit an execution unit.
Code Units
A code unit is a discrete unit of work within a transaction. For example, a trigger is one unit of code, as is a webservice method or a validation rule. CODE_UNIT_STARTED and CODE_UNIT_FINISHED delimit units of code. Units of work can embed other units of work. For example:
EXECUTION_STARTED
CODE_UNIT_STARTED|[EXTERNAL]execute_anonymous_apex
CODE_UNIT_STARTED|[EXTERNAL]MyTrigger on Account trigger event BeforeInsert for [new]|sfdc_trigger/MyTrigger
CODE_UNIT_FINISHED <– The trigger ends
CODE_UNIT_FINISHED <– The executeAnonymous ends
EXECUTION_FINISHED
Units of code include, but aren’t limited to, the following:
Triggers
Workflow invocations and time-based workflow
Validation rules
Approval processes
Apex lead convert
@future method invocations
Web service invocations
executeAnonymous calls
Visualforce property access on Apex controllers
Visualforce actions on Apex controllers
Execution of the batch Apex start and finish methods, and each execution of the execute method
Execution of the Apex System.Schedule execute method
Incoming email handling
Log Lines
Log lines are included inside units of code and indicate which code or rules are being executed. Log lines can also be messages written to the debug log.
More Log Data
In addition, the log contains the following information.
Cumulative resource usage is logged at the end of many code units. Among these code units are triggers, executeAnonymous, batch Apex message processing, @future methods, Apex test methods, Apex web service methods, and Apex lead convert.
Cumulative profiling information is logged once at the end of the transaction and contains information about DML invocations, expensive queries, and so on. “Expensive” queries use resources heavily.
Heap usage is accurately reported in the debug log and an exception is thrown whenever an Apex Heap Size error occurs. At other times, the heap size shown in the debug log is the largest heap size that was calculated during the transaction. To reduce the overhead on small transactions, minimal heap usage doesn’t warrant an accurate calculation and is reported as 0(zero).
Setting Debug Log Filters for Apex Classes and Triggers
Debug log filtering provides a mechanism for fine-tuning the log verbosity at the trigger and class level. This is especially helpful when debugging Apex logic. For example, to evaluate the output of a complex process, you can raise the log verbosity for a given class while turning off logging for other classes or triggers within a single request.
When you override the debug log levels for a class or trigger, these debug levels also apply to the class methods that your class or trigger calls and the triggers that get executed as a result. All class methods and triggers in the execution path inherit the debug log settings from their caller, unless they have these settings overridden.
The following diagram illustrates overriding debug log levels at the class and trigger level. For this scenario, suppose Class1 is causing some issues that you want to take a closer look at. To this end, the debug log levels of Class1 are raised to the finest granularity. Class3 doesn’t override these log levels, and therefore inherits the granular log filters of Class1. However, UtilityClass has already been tested and is known to work properly, so it has its log filters turned off. Similarly, Class2 isn’t in the code path that causes a problem, therefore it has its logging minimized to log only errors for the Apex Code category. Trigger2 inherits these log settings from Class2.
New Trace Flag
Setup > Debug Logs
To specify the type of information that is included in debug logs, add trace flags and debug levels. Each trace flag includes a debug level, a start time, an end time, and a log type.
Trace flags set logging levels (such as for Database, Workflow, and Validation) for a user, Apex class, or Apex trigger for up to 24 hours.
Select Automated Process from the dropdown list to set a trace flag on the automated process user. The automated process user runs background jobs, such as emailing Chatter invitations.
Select Platform Integration from the dropdown list to set a trace flag on the platform integration user. The platform integration user runs processes in the background, and appears in audit fields of certain records, such as cases created by the Einstein Bot.
Select User from the dropdown list to specify a user whose debug logs you’d like to monitor and retain.
Select Apex Class or Apex Trigger from the dropdown list to specify the log levels that take precedence while executing a specific Apex class or trigger. Setting class and trigger trace flags doesn’t cause logs to be generated or saved. Class and trigger trace flags override other logging levels, including logging levels set by user trace flags, but they don’t cause logging to occur. If logging is enabled when classes or triggers execute, logs are generated at the time of execution.
Working with Logs in the Developer Console
Use the Logs tab in the Developer Console to open debug logs.
Logs open in Log Inspector. Log Inspector is a context-sensitive execution viewer in the Developer Console. It shows the source of an operation, what triggered the operation, and what occurred next. Use this tool to inspect debug logs that include database events, Apex processing, workflow, and validation logic.
When using the Developer Console or monitoring a debug log, you can specify the level of information that gets included in the log.
Log category
The type of information logged, such as information from Apex or workflow rules.
Log level
The amount of information logged.
Event type
The combination of log category and log level that specify which events get logged. Each event can log additional information, such as the line and character number where the event started, fields associated with the event, and duration of the event.
Debug Log Categories
Each debug level includes a debug log level for each of the following log categories. The amount of information logged for each category depends on the log level.
Log Category
Database Includes information about database activity, including every data manipulation language (DML) statement or inline SOQL or SOSL query.
Workflow Includes information for workflow rules, flows, and processes, such as the rule name and the actions taken.
NBA Includes information about Einstein Next Best Action activity, including strategy execution details from Strategy Builder.
Validation Includes information about validation rules, such as the name of the rule and whether the rule evaluated true or false.
Callout Includes the request-response XML that the server is sending and receiving from an external web service. Useful when debugging issues related to using Lightning Platform web service API calls or troubleshooting user access to external objects via Salesforce Connect.
Apex Code Includes information about Apex code. Can include information such as log messages generated by DML statements, inline SOQL or SOSL queries, the start and completion of any triggers, and the start and completion of any test method.
Apex Profiling Includes cumulative profiling information, such as the limits for your namespace and the number of emails sent.
Visualforce Includes information about Visualforce events, including serialization and deserialization of the view state or the evaluation of a formula field in a Visualforce page.
System Includes information about calls to all system methods such as the System.debug method.