Skip to content

Angular Component Testing

Component tests render a component's actual template and verify both its behavior and its rendered output. This lesson covers TestBed, ComponentFixture, and common DOM assertion patterns.

What Is a Component Test?

A component test uses Angular's TestBed to create a real instance of a component along with its template, rendered into a detached DOM tree. This lets you assert on rendered output, simulate user interaction, and verify inputs/outputs, closer to how the component actually behaves in a real app than a pure unit test of the class alone.

ComponentFixture, returned by TestBed.createComponent(), is the main object you interact with: it exposes the component instance, its native DOM element, and methods to trigger change detection.

describe('CounterComponent', () => {
  let fixture: ComponentFixture<CounterComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({ imports: [CounterComponent] });
    fixture = TestBed.createComponent(CounterComponent);
    fixture.detectChanges();
  });

  it('increments the count when the button is clicked', () => {
    const button = fixture.nativeElement.querySelector('button');
    button.click();
    fixture.detectChanges();

    expect(fixture.nativeElement.textContent).toContain('1');
  });
});

Because CounterComponent is standalone, it's added to imports rather than declarations in the testing module.

Core Component Testing APIs

TestBed.configureTestingModule({ imports: [MyComponent] });
const fixture = TestBed.createComponent(MyComponent);
fixture.componentInstance.someInput = value;
fixture.detectChanges();
fixture.nativeElement.querySelector('.selector');
fixture.debugElement.query(By.css('.selector'));
  • TestBed.createComponent() creates a ComponentFixture wrapping a real component instance and its rendered DOM.
  • fixture.detectChanges() must be called to apply bindings after setting inputs or triggering state changes.
  • fixture.nativeElement gives raw DOM access; fixture.debugElement gives Angular-aware querying via By.css()/By.directive().
  • Standalone components go in the testing module's imports, not declarations.

Component Testing Cheatsheet

Common assertions and interactions in component tests.

Task Example
Create a component fixture TestBed.createComponent(MyComponent)
Apply bindings fixture.detectChanges()
Set an input directly fixture.componentInstance.value = 'x'
Query the DOM (raw) fixture.nativeElement.querySelector('.class')
Query the DOM (Angular-aware) fixture.debugElement.query(By.css('.class'))
Simulate a click button.click() then fixture.detectChanges()
Spy on an output fixture.componentInstance.saved.subscribe(spy)

Testing Inputs and Outputs

Component tests can set inputs directly on fixture.componentInstance (bypassing a host template) and subscribe to outputs to assert they emit the expected value in response to interaction.

it('emits ratingChange when a star is clicked', () => {
  const fixture = TestBed.createComponent(RatingComponent);
  fixture.componentInstance.max = 5;
  fixture.detectChanges();

  const emitted: number[] = [];
  fixture.componentInstance.ratingChange.subscribe((v: number) => emitted.push(v));

  const fifthStar = fixture.nativeElement.querySelectorAll('button')[4];
  fifthStar.click();

  expect(emitted).toEqual([5]);
});

Testing With a Wrapper Host Component

For components that need to be tested more like they're actually used, receiving inputs via real property binding, a small test-only host component wrapping the component under test gives a more realistic setup.

@Component({
  standalone: true,
  imports: [RatingComponent],
  template: '<app-rating [max]="max" (ratingChange)="lastRating = $event"></app-rating>',
})
class HostComponent {
  max = 5;
  lastRating?: number;
}

Mocking Services Injected Into a Component

Just like unit tests for services, component tests use TestBed's providers array to substitute real dependencies (like an API service) with test doubles, keeping the test fast and focused on the component's own logic.

TestBed.configureTestingModule({
  imports: [UserListComponent],
  providers: [{ provide: UserService, useValue: { getUsers: () => of([{ id: '1', name: 'Ada' }]) } }],
});

Common Mistakes

  • Forgetting to call fixture.detectChanges() after changing an input or triggering a state change, then asserting on stale DOM.
  • Testing a standalone component by adding it to declarations instead of imports in the testing module.
  • Reaching for real services (like real HttpClient calls) in component tests instead of mocking dependencies.
  • Over-relying on brittle CSS selectors for querying the DOM instead of stable test attributes or By.directive().

Key Takeaways

  • Component tests render a real component and template using TestBed and ComponentFixture.
  • fixture.detectChanges() must be called to apply bindings after any state change during a test.
  • Inputs can be set directly, or more realistically through a small test-only host component.
  • Mock injected service dependencies to keep component tests fast and focused.

Pro Tip

For components with meaningful input/output-driven behavior, prefer wrapping them in a small host test component over setting fixture.componentInstance properties directly, it exercises the component the way it's actually used in real templates and catches binding-related bugs a direct property assignment would miss.