C#: dependency injection makes code testable

Pass a dependency in through the constructor so tests can replace it with a fixed one.

C#
using System;

interface IClock {
  int Hour { get; }
}

class SystemClock : IClock {
  public int Hour { get { return DateTime.Now.Hour; } }
}

class FixedClock : IClock {
  public FixedClock(int hour) {
    Hour = hour;
  }

  public int Hour { get; }
}

class Greeter {
  private readonly IClock clock;

  public Greeter(IClock clock) {
    this.clock = clock;
  }

  public string Greet() {
    return clock.Hour < 12 ? "Good morning" : "Good afternoon";
  }
}

class Program {
  static void Main() {
    Console.WriteLine(new Greeter(new FixedClock(9)).Greet());
    Console.WriteLine(new Greeter(new FixedClock(15)).Greet());
  }
}
Output
Good morning
Good afternoon

Explanation

Dependency injection means a class receives the objects it depends on instead of creating them. If Greeter called DateTime.Now itself, its output would change with the time of day, and a test could not control it. Depending on an interface lets callers supply any implementation.

In production you would pass a clock that reads the real time. In a test you pass a fake clock fixed at the hour you need, so the result is deterministic. This is the dependency inversion principle: high-level code depends on an abstraction, not on a concrete class. Frameworks such as Microsoft.Extensions.DependencyInjection wire the objects together automatically.

Explore the dry run
Guided walkthroughReady
1using System;
2
3interface IClock {
4 int Hour { get; }
5}
6
7class SystemClock : IClock {
8 public int Hour { get { return DateTime.Now.Hour; } }
9}
10
11class FixedClock : IClock {
12 public FixedClock(int hour) {
13 Hour = hour;
14 }
15
16 public int Hour { get; }
17}
18
19class Greeter {
20 private readonly IClock clock;
21
22 public Greeter(IClock clock) {
23 this.clock = clock;
24 }
25
26 public string Greet() {
27 return clock.Hour < 12 ? "Good morning" : "Good afternoon";
28 }
29}
30
31class Program {
32 static void Main() {
33 Console.WriteLine(new Greeter(new FixedClock(9)).Greet());
34 Console.WriteLine(new Greeter(new FixedClock(15)).Greet());
35 }
36}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
The abstraction that Greeter depends on.—
—
The dependency is supplied from outside, through the constructor.—
—
A fake clock fixed at 9 o'clock makes the result predictable.Hour = 9
Good morning
The same class gives a different answer for 15 o'clock.Hour = 15
Good morning
Good afternoon

Cost and common mistakes

O(1) per call.

  • Creating the dependency inside the class with new makes tests slow, random, or dependent on the environment.
  • Do not create an interface for every class. Introduce one where behavior needs to vary or be replaced in tests.

Try it yourself

Where would SystemClock be used?

Reveal answer and reasoning

In the real program: new Greeter(new SystemClock()) reads the actual time, while tests use FixedClock.

Further reading