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.
@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
# 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.
@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.
// 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:
# 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
if/else are covered by separate test methods
@TestSetup to share data across all test methods in a class — faster and DRY
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.
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.