home Homebuild Toolsbug_report Errorsmenu_book Guideslightbulb Tipssmart_toy Promptsextension Extensionsfolder_open Resourcesinfo About
search
error

Deployment failed: Average test coverage across all Apex Classes and Triggers is 62%, at least 75% test coverage is required.

What does this error mean?

Before any Apex code can be deployed to a Salesforce production org, Salesforce runs all local test classes and calculates the percentage of Apex code lines that are executed by those tests. If the overall coverage falls below 75%, the deployment is blocked entirely.

Coverage is calculated across all Apex classes and triggers in the org — not just the classes being deployed. A new class with 0% coverage can pull the org-wide average below the threshold even if the rest of the org is well-covered.

Coverage thresholds at a glance

Status Coverage Result
❌ Below threshold < 75% Deployment will fail
⚠️ Minimum = 75% Passes but leaves no margin
✅ Healthy 85%+ Safe to deploy with buffer
Rule Detail Required?
Org-wide coverage ≥ 75% Average across all classes and triggers in the target org Required
Each trigger ≥ 1% Every trigger must be covered by at least one test line Required
Test classes excluded Classes annotated @isTest don't count toward coverage Info
System.assert() Not required for coverage, but strongly recommended for quality Info
Sandbox deploys Coverage not enforced — tests run but threshold not required Info

Common Causes

1. New Class Deployed with No Test Coverage

The most common scenario. A developer writes a new Apex class but skips the test class. In sandbox this isn't enforced, so it goes unnoticed — until production deployment, where the class brings the org-wide average down.

2. Test Classes That Don't Call the Actual Code

Tests that insert records without triggering automation, or test classes that only test trivial getter/setter methods, don't cover the real logic inside handler classes, service classes, or utilities.

Apex — Bad Test (No Real Coverage)
@isTest
private class AccountServiceTest {
    @isTest
    static void testNothing() {
        // ❌ BAD: Creates record but never calls AccountService
        // AccountService methods have 0% coverage
        Account acc = new Account(Name = 'Test');
        insert acc;
        System.assertNotEquals(null, acc.Id);
    }
}

3. Missed Branch Coverage — if/else and Exception Paths

A single test covering the "happy path" leaves the else branch, catch blocks, and edge cases uncovered. Each uncovered branch is an uncovered line that reduces your percentage.

4. Dead Code That Can't Be Tested

Commented-out code that was accidentally left in a class counts as uncovered lines — even though it will never execute. Commented Apex lines that aren't removed still reduce your coverage percentage.

5. Deleted Test Classes Leaving Orphaned Production Code

Removing a test class while the production code it covered remains in the org instantly drops that code's coverage to 0%, potentially pulling the org-wide total below 75%.

How to Find Uncovered Lines

Method 1: VS Code — Apex Test Runner

Run tests from the VS Code sidebar using the Salesforce Extension Pack. After tests complete, click View Coverage — uncovered lines are highlighted in red directly in your editor. This is the fastest way to spot gaps.

Method 2: Developer Console

Open the Developer Console → Test → Run All Tests. After execution, go to the Tests tab → select a class → click the Coverage percentage link to see a colour-coded line-by-line view of covered (blue) vs. uncovered (red) lines.

Method 3: SFDX CLI

Terminal — SFDX
# Run all tests and output coverage report
sf apex run test \
  --test-level RunAllTestsInOrg \
  --code-coverage \
  --result-format human \
  --output-dir ./coverage-report

# Run tests for a specific class only
sf apex run test \
  --class-names AccountServiceTest \
  --code-coverage \
  --result-format json

How to Fix It

Solution 1: Write a Proper Test Class That Calls Your Code

Every service class, handler, and utility must be called directly from a test method. Use Test.startTest() and Test.stopTest() to isolate governor limits and call the actual methods — not just insert records and hope the trigger fires.

Apex — Good Test Class
@isTest
private class AccountServiceTest {

    @TestSetup
    static void makeData() {
        insert new Account(Name = 'Test Corp', Phone = '415-555-0100');
    }

    @isTest
    static void testGetActiveAccounts_returnsResults() {
        Test.startTest();
        // ✅ GOOD: Directly calls the method being tested
        List<Account> results =
            AccountService.getActiveAccounts();
        Test.stopTest();

        System.assertNotEquals(0, results.size(),
            'Should return at least one active account');
    }

    @isTest
    static void testGetActiveAccounts_emptyOrg() {
        // ✅ Test the edge case — no active accounts
        delete [SELECT Id FROM Account];

        Test.startTest();
        List<Account> results =
            AccountService.getActiveAccounts();
        Test.stopTest();

        System.assertEquals(0, results.size(),
            'Should return empty list when no active accounts');
    }
}

Solution 2: Cover Both Branches of Every if Statement

For every conditional in your code, write at least two tests — one that exercises the if branch and one that exercises the else branch. Don't forget catch blocks and null-check paths.

Apex — Branch Coverage Pattern
// Method under test
public static String classify(Account acc) {
    if (acc.AnnualRevenue > 1000000) {
        return 'Enterprise';       // Branch A
    } else if (acc.AnnualRevenue > 100000) {
        return 'Mid-Market';        // Branch B
    } else {
        return 'SMB';               // Branch C
    }
}

// Test covering all three branches
@isTest
static void testClassify_allBranches() {
    System.assertEquals('Enterprise',
        AccountService.classify(
            new Account(AnnualRevenue = 5000000)));

    System.assertEquals('Mid-Market',
        AccountService.classify(
            new Account(AnnualRevenue = 500000)));

    System.assertEquals('SMB',
        AccountService.classify(
            new Account(AnnualRevenue = 50000)));
}

Solution 3: Validate Before Deploying — Run Tests in Sandbox First

Always run the full test suite in a sandbox that mirrors production before attempting a production deployment. Use the SFDX CLI to validate without actually deploying:

Terminal — Validate-Only Deployment
# Validate without deploying — checks coverage against production rules
sf project deploy validate \
  --source-dir force-app \
  --test-level RunLocalTests \
  --target-org production \
  --code-coverage

# Quick deploy if validation already passed (within 10 days)
sf project deploy quick \
  --job-id <validation-job-id> \
  --target-org production

Deployment Testing Checklist

radio_button_unchecked Org-wide coverage is ≥ 75% in a full-copy sandbox before deploying
radio_button_unchecked Every trigger has at least one test covering it (≥ 1 line executed)
check_circle Every new class has a corresponding test class calling it directly
check_circle Both branches of every if/else are covered by separate test methods
check_circle Catch blocks and exception paths are tested with deliberate error scenarios
lightbulb Aim for 85%+ to give yourself a buffer against future additions lowering the average
lightbulb Use @TestSetup to share data across all test methods in a class — faster and DRY
warning

Coverage ≠ Quality. Lines executed during a test count as "covered" even with no assertions. A test that calls every method but asserts nothing gives you 100% coverage and zero confidence. Always add meaningful System.assertEquals() assertions to validate actual behaviour.

lightbulb

Pro Tip: Use the Apex Test Execution page in Setup → Apex Test Execution → View Test History to see a class-by-class breakdown of coverage percentages without leaving the browser. Clicking any class name shows uncovered lines inline — no VS Code or CLI required.