Advanced 30 min read

Dependency Injection in Apex

By the end of this lesson, you'll be able to:

  • Define dependency injection and explain the problem it solves
  • Refactor a class to accept its selector as a constructor parameter
  • Use dependency injection to substitute a fake selector in a unit test

Prerequisites: "Domain Layer and Trigger Handlers Revisited"

The problem: a hardcoded dependency

public with sharing class TransactionBalanceService {
    public static void applyDeltas(List<Transaction__c> transactions) {
        BankAccountSelector selector = new BankAccountSelector(); // hardcoded
        // ...uses selector to query and update balances
    }
}

Because TransactionBalanceService constructs its own BankAccountSelector internally, a test exercising this method has no way to substitute a fake selector — every test is forced to rely on BankAccountSelector's real SOQL query actually running against test data.

Injecting the dependency instead

public with sharing class TransactionBalanceService {
    private BankAccountSelector selector;

    public TransactionBalanceService(BankAccountSelector selector) {
        this.selector = selector;
    }

    public TransactionBalanceService() {
        this(new BankAccountSelector()); // sensible real-world default
    }

    public void applyDeltas(List<Transaction__c> transactions) {
        // ...uses this.selector instead of constructing one internally
    }
}

The no-argument constructor still exists for normal production use (new TransactionBalanceService()), but a test can now call new TransactionBalanceService(fakeSelector) and pass in a test-only substitute — this is the entire idea of dependency injection, in nine lines of Apex.

When this actually pays off

For a class whose dependency is a simple selector, this benefit is modest — Module 42's tests already worked fine inserting real records. Dependency injection earns its keep more clearly when a dependency is expensive or hard to set up in a test at all (an external callout, Module 36, or a slow batch process, Module 38) — situations where substituting a fake becomes genuinely necessary rather than just convenient.

Exercise

As a comment, explain why TransactionBalanceService keeps a no-argument constructor that calls this(new BankAccountSelector()), rather than only having the constructor that requires a selector parameter.

Show hint

Think about what ordinary production code (like a trigger handler) would look like calling this class.

APEX

Dependency Injection in Apex Quiz

1. What problem does dependency injection solve, as shown in this lesson?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

Dependency injection means a class receives its dependencies (like a selector) from outside, rather than constructing them internally — a small change that makes it possible to substitute a fake dependency during testing.