Skip to main content

๐Ÿ›‘ Salesforce Code Coverage vs. Actual Test Quality (Stop Chasing 75%)

๐Ÿ’ฌ In plain words: 75% code coverage is simply the entry ticket to deploy to production, not the ultimate goal. An Apex test without assertions simply runs code lines without proving anything works. High-quality tests verify OUTCOMES, handle bulk processing (200 records), validate error paths, and run under specific user profiles.

๐Ÿ”‘ Key Points

  • Coverage Is Not Quality: Code coverage only counts the lines of code executed. It does not guarantee that the logic is correct.
  • Assertions Are Mandatory: Use the modern Assert class (e.g., Assert.areEqual()) to verify expected outcomes.
  • Bulkification Testing: Always test how your code handles 200 records at once, not just a single record.
  • Negative Testing: Ensure your tests intentionally trigger and catch expected exceptions (like validation rule failures).
  • Context Matters: Use System.runAs() to verify that sharing rules and Field-Level Security (FLS) are strictly enforced.
๐ŸŽฌ Real-Life Example: The 92% Coverage Illusion

Imagine a Salesforce org that proudly boasts 92% overall code coverage. Yet, a massive pricing bug still slips into production.

The Old/Bad Way: Developers write tests that merely invoke methods. One "test" executes a batch job and immediately finishes without a single assertion.

Why it fails: The platform counts the executed lines, but no behavior is verified. An assert-free test is like installing a smoke detector without a battery.

The Modern/Good Way:
  • Every single test explicitly asserts an outcome: Assert.areEqual(expectedPrice, quote.Total__c, 'Pricing calculation failed');
  • Tests insert 200 records to prove bulkification limits hold up.
  • Tests intentionally pass bad data to prove the correct error is thrown.
  • CI/CD pipelines automatically reject pull requests containing assert-free tests.
The Payoff: The test suite stops being a vanity metric and becomes a genuine safety net. The next pricing bug dies in the sandbox, not in production.
๐Ÿง  Ticket, Not Trophy: 75% is just the entry ticket. True test quality means asserting outcomes, testing bulk data, and validating error paths.

Concept: Why Coverage Statistics Lie

In the Salesforce ecosystem, 75% code coverage is a hard deployment gate enforced by the platform. Unfortunately, many teams treat this number as a quality metric. It is not.

  • Coverage strictly counts lines executed. It never checks if the behavior is correct.
  • A test that lacks assertions—or one that only asserts that no exception was thrown—can easily green-light entirely broken business logic.
  • When architects and interviewers look at your code, they are looking for meaning. They want to see detailed assertions with clear failure messages.
  • They want to see boundary testing, permutation coverage of complex rules, and System.runAs() blocks protecting sensitive data access.
Salesforce Apex Code Coverage vs Actual Test Quality Diagram
๐Ÿงญ 360 Card — Coverage vs. Test Quality
  • Rule: 75% is the entry ticket, not the end goal. The real goal is a test that turns red when the behavior breaks.
  • Gain: A test suite you implicitly trust allows you to deploy on a Friday afternoon without panic. That peace of mind is the true ROI.
  • Price: Meaningful tests take substantial time to write. Furthermore, the overall coverage percentage is often the only metric non-technical leadership understands.
  • Limits: While 75% org-wide is the minimum, every Apex trigger needs at least some coverage. But remember, coverage blindly counts execution, not verification.
  • The Mirror: Chasing an arbitrary 90% target blindly leads to a surge in assert-free tests. The metric climbs, but production keeps breaking.
  • The Fix: Shift your target from overall percentage to "assertion density" in changed code, ensuring zero assert-free tests make it into the codebase.
⚠ INTERVIEW TRAP: 75% is a DEPLOYMENT GATE, not a quality metric. It counts executed lines, never verified behavior. A test with absolutely no assertions will still count toward your 75% requirement. Never refer to 75% as a measure of code quality.

Core Q&A

Q: You inherit an org operating at 92% coverage, yet production bugs continuously ship. How do you audit this?
๐ŸŽฏ Say this first: I would audit for assert density, bulk tests, negative tests, and user profile context. 92% coverage without assertions is purely theater.

A: Start by sampling the test suite to identify common pathologies:

  • Assert-Free Tests: Search the codebase for test methods missing modern Assert methods. You will often find they make up a massive portion of a legacy suite.
  • Happy-Path Monoculture: Look for a lack of negative testing. If error handling and validation logic are never exercised, bugs will slip through.
  • Single-Record Bias: Check if triggers are only tested with one record, rendering bulkification bugs invisible.
  • Missing Context: Check for missing System.runAs() usage. If every test runs as a System Administrator, FLS and sharing rule regressions will bypass your tests entirely.

Once audited, you must re-anchor the development team's definition of a unit test. A test exists to fail when business rules regress. Introduce code review requirements that strictly enforce assertion density and perform "mutation" spot checks (intentionally breaking code in a sandbox to ensure a test actually fails).

Q: What does a properly structured trigger test suite for a single object look like?

A: A robust trigger test suite covers five mandatory categories. Completeness of these scenarios is far more important than just having a high volume of tests:

  • Bulk Testing: Insert or update 200 records at once, and assert the expected outcome on all of them, not just the first record in the list.
  • Branch Coverage: Write a single-record test for each major business logic branch so that when a failure occurs, it is highly readable and isolated.
  • Negative Testing: Intentionally trigger validation rules or errors. Catch the DmlException and assert that the correct error message was surfaced to the user.
  • Recursion Protection: Create an update scenario that forces the trigger to fire twice, proving that your recursion guard mechanism works perfectly.
  • Security Context: Run a test inside a System.runAs() block using a standard, minimally-privileged user to prove the path respects real-world sharing and FLS.
// Example of a proper negative assertion using modern Apex syntax
@isTest
static void testDiscountValidation_NegativePath() {
    Opportunity opp = new Opportunity(Name = 'Test', StageName = 'Prospecting', Discount__c = 150);
    
    Test.startTest();
    try {
        insert opp;
        Assert.fail('Expected a DmlException due to invalid discount');
    } catch (DmlException e) {
        Assert.isTrue(e.getMessage().contains('Discount cannot exceed 100%'), 'Wrong error message returned');
    }
    Test.stopTest();
}
Q: Leadership wants to set a 90% org-wide coverage OKR. How do you counter-propose this?

A: You must redirect the metric toward things that actually predict system safety.

  • Keep the 75%+ as a baseline floor, because the Salesforce platform demands it for deployment.
  • Instead of raw coverage, set the OKR around Assert Density in all newly changed code.
  • Implement a CI/CD linting rule that enforces zero new assert-free tests.
  • Mandate that the top 5 critical business processes have complete critical-path test suites (incorporating the five categories mentioned above).
  • Explain "Goodhart's Law" plainly to leadership: When a measure (like 90% coverage) becomes a target, it ceases to be a good measure. Developers will inevitably game the system by writing assert-free "coverage filler," wasting time making a number look good rather than making the system stable.
Q: How do you enforce test quality in CI/CD pipelines? (Added Question)

A: Automated enforcement is the only way to scale test quality across a growing team.

  • Static Code Analysis: Integrate tools like PMD or SonarQube into your GitHub Actions, Bitbucket Pipelines, or Jenkins flow. Configure rules that flag test methods missing Assert statements.
  • Automated Pull Request Blocking: If the static analysis fails, the PR cannot be merged into the integration branch.
  • Delta Testing: Use tools that run tests only on the modified code components. This speeds up the pipeline while ensuring developers are heavily scrutinizing the specific tests related to their immediate changes.
Q: What does a true Architect-level test strategy look like, beyond just Apex unit tests?
๐ŸŽฏ Say this first: Apex unit testing is only layer one. A true test architecture spans from mocked integrations all the way to automated UI regression suites.

A: A mature, architect-level test strategy requires five distinct layers:

  • Unit Tests: Assert core behavior using a Test Data Factory. Never rely on production data, and never use @isTest(SeeAllData=true).
  • Mocked Integrations: Use HttpCalloutMock so the suite runs predictably without relying on the uptime of real external endpoints.
  • Flow Automation Tests: Utilize Salesforce's native Flow Tests for declarative logic, reserving Apex-driven tests for complex orchestration.
  • UI Automation: Implement targeted Selenium or Playwright tests exclusively over critical business paths. (Attempting 100% UI coverage is a massive maintenance trap).
  • Performance & UAT: Run volume tests in a Full Copy Sandbox before major go-lives, paired with business sign-off via scripted User Acceptance Testing (UAT) scenarios.