35

This is a canonical question and answer developed by the community to help address common questions. If you've been directed here, or your question has been closed as a duplicate, please look through the resources here and use them to shape more specific questions. To browse all canonical questions and answers, including more unit test resources, navigate to the canonical-qa tag.

This canonical question is intended to address several classes of common questions by providing a quick summary and links to comprehensive resources:

  • How is code coverage obtained and calculated?
  • How do I increase my coverage?
  • Why can't I cover these lines?
  • Why isn't the body of my loop or conditional statement covered?
0

1 Answer 1

35

Code coverage is a measurement of how many unique lines of your code are executed while the automated tests are running. Code coverage percentage is the number of covered lines divided by the sum of the number of covered lines and uncovered lines.

For purposes of calculating code coverage, only executable lines of code are counted, whether covered or uncovered. Comments and blank lines are not counted, and System.debug() statements and curly braces that appear alone on one line are also not counted.

While writing unit tests, the main emphasis should be given on actually testing the logic and behavior of the block by designing input data to ensure the code path executes and writing assertions to make sure the code's behavior is as expected. Code coverage is a side effect of this testing process.

Fundamentally, to increase your code coverage, you must write functional unit tests for code paths that are not currently covered. For more community resources on writing high quality unit tests, please see this canonical question; read the rest of this question to learn about common coverage scenarios.

Commonly Encountered Scenarios

Control Structures

You may find that your unit tests only cover one branch of a control statement, like an if/else or switch on construct. Here's an example of a unit test that causes this issue.

Main Class

public class SFSEExample {
    public static void updateAccountDetails(Account a) {
        if (a.Industry == 'Finance') {
            a.Description = 'NOTE: Finance Industry. ' + a.Description;
        } else {
            a.Description = 'Non-finance industry. ' + a.Description;
        }
    }
}

Test Class

@isTest
public class SFSEExample_TEST {
 @isTest
    public static void runTest() {
        Account a = new Account(Name = 'Test', Industry = 'Finance', Description = 'Desc');
        Test.startTest();
        SFSEExample.updateAccountDetails(a);
        Test.stopTest();
        System.assertEquals('NOTE: Finance Industry. Desc', a.Description);
    }
}

Code Coverage

Code coverage snapshot

Note that only one code path is covered by this unit test.

In branching code, you won't be able to ensure that diverging logical paths all execute in a single unit test. Trying to test too much in a single test method tends to make unit tests more difficult to understand and more fragile.

In most cases, you should expect to cover different branches of your logic using multiple unit tests, each of which performs different setup and data creation steps to execute and validate a different code pathway.

Loops

Code coverage that appears to "stop" and not enter the body of a loop, such as a for or while, stems from the same cause as with control structures: the test environment doesn't meet the preconditions for the loop body to execute. Here's an example:

for (Account a : [SELECT Id, Name FROM Account WHERE Name LIKE 'ACME%']) {
    a.Description = 'Here are some more details.';
}

After running unit tests, if the line starting with a.Description is not covered, it's an indication that the loop never begins iterating because the query returns no records. This is a failure of the test data setup: records weren't created that would exercise the functionality of this specific code path.

The same interpretation applies to while and do/while loops.

Batch Apex

If a Batch Apex class is run correctly in test context by using a pattern like

Test.startTest();
Database.executeBatch(myBatch);
Test.stopTest();

and the execute() method is not covered, this almost always indicates that the query run in the start() method returns no records, just as with loops over query results (above).

Exception Handlers

Code within the catch block of an exception handler is executable and must be covered. In order to effectively cover and validate the behavior of your exception handlers, you must design unit test cases that will cause your code to fail and enter error-handling pathways.

Some common strategies to cover exception handlers include:

  • Passing known bad parameters to methods, or inserting bad data in the unit test context, to cause a failure.
  • Using the @TestVisible annotation to expose state and allow it to be manipulated in ways that would not be possible in production code.
  • Applying dependency injection to force database actions, queries, or calls to other code to fail in specific ways.
  • Removing exception handlers that cannot catch any real exception.

Because covering exception handlers is quite case-specific, consider asking a detailed question that focuses closely on your exception handler and attempted test code to get assistance designing a strategy.

Rollbacks & .addError()

Code that purposely adds an error to prevent a database operation or leverages Savepoint/rollback will need to be covered. This would typically entail:

  • Passing the data needed to reach the scenario in the code where the error is added or the rollback occurs.
  • Assert that your test data is as expected (no changes) or an error was thrown when you attempt to perform the relevant DML operation.
  • For asserting errors thrown, you may use:
    • allOrNone with Database.insert(yourTestRecord, false) which would provide you with the errors to verify (Database.Error).
    • Try/Catch blocks surrounding your DML in your test class to catch your expected error thrown and assert its details. Likewise, you may assert no error was thrown in the opposite scenario.

Properties

Apex properties with defined getter and setter methods, such as

public Integer index { get; set; }

are considered executable lines of code. This is true even if, as depicted here, the getter and setter are synthesized by the compiler. You must cover these lines by "calling" the getter and setter method - accessing and setting the property's value.

Resources

For general resources on how to write high quality unit tests that generate code coverage, see this canonical question.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .