liveBook · Manning

Ambient context

As explained in Unit Testing Principles, Practices, and Patterns

Ambient context is a dependency-acquisition pattern in which code obtains a shared dependency through static or globally accessible methods instead of explicit injection.

Ambient context acquires a shared dependency through static or globally accessible methods rather than explicit injection. It hides the dependency, makes changes harder, and complicates testing. section 8.6 section 11.6

Treat the pattern as a warning sign, not a convenience. If explicit injection is too awkward, reduce excessive logging or unnecessary layers of indirection instead of hiding the dependency. section 8.6

Prefer these alternatives:

  • Inject a logger through a method argument or constructor. section 8.6
  • Inject time explicitly as a plain value when possible; otherwise inject a service at the operation boundary and pass its value inward. section 11.6
  • Move cross-cutting behavior toward domain events when direct logging creates excessive coupling. section 8.6

Static time versus explicit time

A DateTimeServer with static Now and Init methods lets production code and tests replace the clock globally. An injected IDateTimeServer instead supplies time through InquiryController, which passes the resulting DateTime value to the domain operation. section 11.6

Listing 11.16. Current date and time as an ambient context
public static class DateTimeServer
{
    private static Func<DateTime> _func;
    public static DateTime Now => _func();

    public static void Init(Func<DateTime> func)
    {
        _func = func;
    }
}

DateTimeServer.Init(() => DateTime.Now);                  #1

DateTimeServer.Init(() => new DateTime(2020, 1, 1));      #2

Compare the static clock setup with the explicit dependency version to see how shared test state becomes an injected collaborator. section 11.6

How Unit Testing Principles, Practices, and Patterns covers it

How deep it goes
Defines the pattern, explains its testing and maintenance costs, and contrasts it with explicit injection without detailing runtime internals.
Examples
Uses static loggers, a configurable DateTimeServer, explicit logger parameters, injected time services, and domain events.
What it assumes
Assumes familiarity with dependencies, static methods, unit testing, constructors, and method arguments.
What you can do after
Diagnose hidden dependencies and replace them with explicit logger, time, or event-based designs that are easier to test.
How it connects
Connects dependency injection with unit testing, static methods, domain events, and shared test state.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Unit Testing Principles, Practices, and Patterns. The section links point to the parts of the book it draws on.