Why Is My Code Coverage Low and How Can I Fix It?

Why Is My Code Coverage Low and How Can I Fix It?

On July 4, 2025, Posted by , In Apex,Salesforce, By ,, , With Comments Off on Why Is My Code Coverage Low and How Can I Fix It?
Why Is My Code Coverage Low and How Can I Fix It

Question

Code coverage is a measurement of how many unique lines of code are executed when running automated tests. The percentage is calculated

Code Coverage = Covered Lines/ Covered Lines+Uncovered Lines * 100

Only executable lines are counted—comments, blank lines, System.debug() statements, and standalone curly braces {} are ignored. However, even after writing test methods, certain lines remain uncovered. Why does this happen, and how can I increase my code coverage effectively?

Answer

To increase code coverage, you must write functional unit tests that execute the untested code paths. While achieving high coverage is useful, the primary focus should be testing logic and behavior through well-designed test cases with proper assertions. Below are some common scenarios where code coverage issues arise and how to address them:

1. Conditional Statements (if/else, switch)

If your unit test only covers one branch of a conditional statement, the other branch remains uncovered.

Example:

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 (Only Partial Coverage):

@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);
    }
}

Here, only the if block executes because the test sets Industry = 'Finance'. The else block remains uncovered.

Fix: Write another test case where Industry is not 'Finance' to ensure full coverage.

@isTest
public class SFSEExample_TEST {
    @isTest
    public static void testFinanceIndustry() {
        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);
    }

    @isTest
    public static void testNonFinanceIndustry() {
        Account a = new Account(Name = 'Test', Industry = 'Healthcare', Description = 'Desc');
        Test.startTest();
        SFSEExample.updateAccountDetails(a);
        Test.stopTest();
        System.assertEquals('Non-finance industry. Desc', a.Description);
    }
}

2. Loops (for, while, do-while)

If a loop is not executed during tests, its body remains uncovered.

Example:

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

If no Account records exist that match the query, the loop never runs, leaving its body uncovered.

Fix: Ensure test data setup includes records that satisfy the query conditions:

@isTest
public class LoopTest {
    @isTest
    public static void testLoop() {
        Account testAccount = new Account(Name = 'ACME Corp');
        insert testAccount;

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

3. Batch Apex Coverage

If a Batch Apex class’s execute() method isn’t covered, it’s often because start() returns no records.

Example

public class MyBatch implements Database.Batchable<SObject> {
    public Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT Id FROM Account WHERE Name LIKE \'ACME%\'');
    }

    public void execute(Database.BatchableContext BC, List<SObject> scope) {
        for (Account a : (List<Account>) scope) {
            a.Description = 'Batch Updated';
        }
        update scope;
    }

    public void finish(Database.BatchableContext BC) {}
}

Fix: Insert test records before executing the batch:

@isTest
public class MyBatchTest {
    @isTest
    public static void testBatch() {
        Account testAccount = new Account(Name = 'ACME Corp');
        insert testAccount;

        Test.startTest();
        MyBatch batch = new MyBatch();
        Database.executeBatch(batch);
        Test.stopTest();
    }
}

4. Exception Handling

Code inside a catch block is executable and must be covered by tests.

Example (Uncovered Catch Block):

public class ExceptionExample {
    public static void riskyMethod() {
        try {
            Integer x = 5 / 0; // Division by zero error
        } catch (Exception e) {
            System.debug('Exception caught: ' + e.getMessage());
        }
    }
}

Fix: Write a test case that triggers the exception.

@isTest
public class ExceptionExampleTest {
    @isTest
    public static void testExceptionHandling() {
        Test.startTest();
        ExceptionExample.riskyMethod();
        Test.stopTest();
    }
}

5. Savepoint and addError() Coverage

Apex methods that use addError() or rollbacks need explicit tests.

Example:

public class AccountTriggerHandler {
    public static void validateAccount(Account a) {
        if (a.Name == 'Restricted') {
            a.addError('This name is not allowed.');
        }
    }
}

Fix: Use Database.insert(yourTestRecord, false) to capture errors:

@isTest
public class AccountTriggerHandlerTest {
    @isTest
    public static void testAddError() {
        Account a = new Account(Name = 'Restricted');

        Database.SaveResult result = Database.insert(a, false);
        System.assert(!result.isSuccess(), 'Insert should fail');
        System.assertEquals('This name is not allowed.', result.getErrors()[0].getMessage());
    }
}

6. Covering Properties

Properties with explicit or implicit getters/setters are executable and require coverage.

Example:

public class PropertyExample {
    public Integer index { get; set; }
}

Fix: Access the property in your test:

@isTest
public class PropertyExampleTest {
    @isTest
    public static void testProperty() {
        PropertyExample obj = new PropertyExample();
        obj.index = 5;
        System.assertEquals(5, obj.index);
    }
}

Code coverage issues are often caused by missing test data, untested conditional paths, and overlooked edge cases. By understanding these patterns and writing targeted unit tests, you can increase your code coverage while ensuring meaningful test coverage of logic and behavior.

Real-Time Project-Based Salesforce Training to Kick Start Your Career

Our Salesforce course is thoughtfully designed to provide a comprehensive understanding of the Salesforce platform, equipping you with the essential skills to succeed in the CRM industry. The program covers vital modules like Salesforce Admin, Developer, and AI, combining theoretical learning with hands-on application. By engaging in real-world projects and interactive exercises, you’ll gain the expertise to solve complex business challenges using Salesforce solutions. Our seasoned instructors ensure you develop both technical proficiency and industry knowledge to thrive in the Salesforce ecosystem.

Beyond technical expertise, our Salesforce Training in New York includes personalized mentorship, certification support, and interview preparation to boost your career prospects. You’ll gain access to in-depth study resources, real-time project experience, and continuous guidance throughout your learning journey. By the end of the course, you’ll be well-prepared for certification exams and possess the problem-solving skills employers seek. Take the first step toward a rewarding Salesforce career—enroll in a Free Demo today!

Comments are closed.