skip to content

In Angular, why can a configureTestingModule fake not replace a Clock a component provides itself, and how do you swap it?

level: middleimportance: must knowfreq 58%

answer

  1. two injectors, one wins first
  2. element injector sits below the module
  3. override the component's metadata
  4. set replaces the whole array

basics

~20 s

A component's own providers create Clock in its element injector, which is asked before the testing module, so the module-level fake is never reached. Replace it with TestBed.overrideComponent on the providers metadata or TestBed.overrideProvider(Clock, { useValue }).

solid answer

~40 s

When a component declares `providers: [Clock]`, Angular creates that `Clock` in the component's **element injector**. Resolution walks up from there, so the component finds its own `Clock` before it ever reaches the testing module, and a fake in `configureTestingModule({ providers })` goes unused. Two fixes: `TestBed.overrideComponent(ShiftBanner, { set: { providers: [{ provide: Clock, useValue: fakeClock }] } })` rewrites the component's metadata (`set` replaces the whole array; `remove`/`add` edit it), or `TestBed.overrideProvider(Clock, { useValue: fakeClock })` replaces every provider for that token, including component `providers` and `viewProviders`. Note that `TestBed.inject(Clock)` reads the testing module's injector, not the component's, so keep your own reference to the fake.

code

ts · 12 lines
ts
const fakeClock = {now: () => new Date(2026, 0, 5, 9, 0)};

beforeEach(() => {
  TestBed.configureTestingModule({imports: [ShiftBanner]});
  TestBed.overrideProvider(Clock, {useValue: fakeClock});
});

it('greets the morning shift', async () => {
  const fixture = TestBed.createComponent(ShiftBanner);
  await fixture.whenStable();
  expect(fixture.nativeElement.textContent).toContain('Good morning');
});

go deeper

for a junior

Remember that a component's own providers beat the testing module's, and that overrideComponent or overrideProvider is how you swap them.

for a middle

Explain the element-injector lookup that shadows the module provider, the set/add/remove semantics, and why TestBed.inject does not see component-level instances.

for a senior

Judge when component-level providers are worth the test friction, pick overrideProvider versus overrideComponent deliberately, and keep a handle on the fake instead of fishing it back out.

for a principal

Weigh whether a component should own a service instance at all; component-level providers buy isolation per instance but make every consumer's tests pay an override tax.

## The setup that surprises people A shift banner greets people by time of day. It declares its clock **on the component**, so every banner gets its own: ```ts import {Component, Injectable, inject} from '@angular/core'; @Injectable() export class Clock { now(): Date { return new Date(); } } @Component({ selector: 'app-shift-banner', providers: [Clock], template: `<p>{{ greeting }}</p>`, }) export class ShiftBanner { private readonly clock = inject(Clock); readonly greeting = this.clock.now().getHours() < 12 ? 'Good morning' : 'Good afternoon'; } ``` The first test attempt puts a fake in the testing module: ```ts TestBed.configureTestingModule({ imports: [ShiftBanner], providers: [{provide: Clock, useValue: {now: () => new Date(2026, 0, 5, 9, 0)}}], }); ``` The greeting still follows the real wall clock. The fake is registered, but nothing asks for it. ## Why: two injectors, and the nearer one wins Angular's dependency injection is **hierarchical**: - The **testing module** gets an environment injector. `configureTestingModule({ providers })` registers there. - Each component instance gets an **element injector**. A component's `providers` (and `viewProviders`) register there. - `inject(Clock)` inside `ShiftBanner` starts at the component's element injector and walks **up**, stopping at the first injector that has the token. Because `ShiftBanner` provides `Clock` itself, the lookup stops at its own element injector and constructs the real `Clock`. The testing-module provider sits above that and is shadowed. The official testing guide makes the same point about a component that provides its own service: the testing module's provider for it is irrelevant. ## Fix 1: TestBed.overrideComponent `TestBed.overrideComponent(type, override)` rewrites the component's decorator metadata before compilation. The override object is a `MetadataOverride<Component>` with three keys: | Key | Effect on `providers` | |---|---| | `set` | Replaces the **whole** array with yours; any other component-level provider is gone | | `remove` | Removes the listed entries | | `add` | Appends entries | ```ts TestBed.configureTestingModule({imports: [ShiftBanner]}); TestBed.overrideComponent(ShiftBanner, { set: {providers: [{provide: Clock, useValue: fakeClock}]}, }); ``` Use `set` when `Clock` is the component's only provider. When the component provides several services, prefer `remove: { providers: [Clock] }` plus `add: { providers: [...] }`, or `set` the full list, so you do not silently drop the others. ## Fix 2: TestBed.overrideProvider `TestBed.overrideProvider(token, provider)` "overwrites all providers for the given token", wherever they are declared: - in the testing module and the NgModules or standalone components it imports; - in a component's or directive's `providers`; - in a component's `viewProviders`. It accepts `{ useValue }` or `{ useFactory, deps }` (with an optional `multi` flag), not `useClass`. When several components provide `Clock`, or you do not want to restate a component's other providers, this is the shorter tool: ```ts TestBed.overrideProvider(Clock, {useValue: fakeClock}); ``` With `useValue`, every injector that provided `Clock` hands out the **same** object, so the test can keep a reference to `fakeClock` and move time forward. With `useFactory`, each injector calls the factory and gets its own instance. ## Reading the instance the component actually got `TestBed.inject(Clock)` reads the **testing module's** injector, never the component's element injector. What it returns depends on the fix: - After `overrideComponent` alone, nothing provides `Clock` at module level, so `TestBed.inject(Clock)` throws a `NullInjectorError` (NG0201). - `overrideProvider` also adds the overriding provider to the testing module. With `useValue`, `TestBed.inject(Clock)` therefore returns the same `fakeClock` object; with `useFactory`, it returns a separate instance from the component's. The robust habit is to keep a reference to your `useValue` object, or to ask the component's own injector through the fixture's debug element. ## Directives and viewProviders The same shadowing applies beyond one component: - A **directive** with its own `providers` creates the service on the element it sits on. `TestBed.overrideDirective(TooltipDirective, { set: { providers: [...] } })` is the directive counterpart of `overrideComponent`. - A component's **`viewProviders`** are visible only to its own view, not to projected content. `overrideComponent` can target `viewProviders` the same way it targets `providers`, and `overrideProvider` patches both. - When the token is provided in **several** places at once — a root service that one component also re-provides — `overrideProvider` is the only single call that replaces all of them. Knowing which injector a service actually comes from is the whole skill here; the override call follows from it. ## Ordering Both override methods must run **before** the testing module is instantiated — before `createComponent` or `TestBed.inject`. Calling them afterwards throws. ## Checklist 1. Look at where the service is provided: root, testing module, or the component's own `providers`/`viewProviders`. 2. For component-level providers, use `overrideComponent` or `overrideProvider`. 3. Keep a handle on the fake rather than relying on `TestBed.inject`. 4. Override before the first `createComponent` or `inject`.

  • When would you choose overrideProvider over overrideComponent for this?
    When several components or directives provide the same token, or when a component has other providers you would have to restate with `set`. `overrideProvider` replaces the token everywhere it is provided, including component `providers` and `viewProviders`, in one call. `overrideComponent` is more surgical: it touches only one class's metadata, and can also change its imports or template.
  • Why can't you write TestBed.overrideProvider(Clock, { useClass: FakeClock })?
    `overrideProvider` accepts only `useValue` or `useFactory` with `deps` (plus an optional `multi` flag); there is no `useClass` form. Write `{ useFactory: () => new FakeClock() }` or pass an instance with `useValue`. `overrideComponent` with a providers array does accept a full `{ provide, useClass }` provider.

A shop that keeps its own spare key never calls the building's front desk, so leaving a spare at the front desk changes nothing for that shop; you have to swap the key the shop itself holds.

saying these in an interview costs you the question

  • Testing-module providers always override a component's own providers.
  • TestBed.inject reads the component's own injector, so it always returns the component's Clock.
  • overrideComponent with set merges the new providers into the existing ones.
  • overrideProvider only affects providers declared in the testing module.
  • The override can be applied after createComponent and takes effect on the next check.