07 Apr 2020
5 min

Spectator – when testing becomes a pleasure

Angular is a nice framework, isn't it? ? But like every tool, it has its weaknesses. A definite shortcoming is, for example, writing integration tests for templates and component classes. The TestBed testing module provided is simply unpleasant.  Can we safely say that such basic things as:

  • mocking up the component dependencies
  • access to a rich set of assertions (only basic ones from Jasmine)
  • catching elements (fixture.debugElement, brrr!!)
  • simple test cases, e.g. if "is button disabled"

Are they simple and clear? For me… definitely not! And at this point, it comes in all white:

spectator

The spectator is a pretty thoughtful abstraction on the built-in angular testing module. The main author is Netanel Basal, who also runs an angular blog – https://netbasal.com, highly recommended! The library makes test writing, readability, and maintenance significantly easier.

The main advantages of the library:

  • support for testing pipes, components, directives, HTTP services and routing
  • simplified catching of DOM tree elements (like in jQuery?)
  • richer API for running events (Mouse helpers and Keyboard helpers)
  • comfortable testing with HostComponent
  • support for ng-content testing
  • more matchers, such as .toBeDisabled() or .toBeFocused()
  • support for entry components and component providers
  • service auto-location, wow!
  • support for IS
  • global queries:
const element = spectator.query('.overlay', {
  root: true,
})

Let’s check it!

Let's compare what the test elements in Spectator might look like compared to the TestBed Module, with simple examples.

  1. Disabled button status:
 // SPECTATOR
  expect(spectator.query('.button')).toBeDisabled();

  // TEST BED
  const button = fixture.debugElement.query(By.css('.button'));
  expect(button.nativeElement.disabled).toBeTruthy();

2. Does *ngFor display 2 components in a list:

  // SPECTATOR
  expect('app-tshirt-item').toHaveLength(2);

  // TEST BED
  const items = fixture.debugElement.queryAll(By.directive(TshirtItemComponent));
  expect(items.length).toBe(2);

3. Locking the service as a component dependency:

// SPECTATOR
  providers: [mockProvider(AuthService)], // mockProvider ze Spectator

  // TEST BED
  providers: [
    {
      provide: AuthService,
      useClass: MockAuthService, // nasza zamockowana klasa
    },
  ]

4. Calling "enter" on input:

 // SPECTATOR
  const input = spectator.query('input');
  spectator.keyboard.pressEnter(input);

  // TEST BED
  const input = fixture.debugElement.query(By.css('input'));
  input.triggerEventHandler('keydown.enter', {});

5. Whether the element contains the text:

// SPECTATOR
const title = spectator.query('h1');
expect(title).toContainText('Page title');

// TEST BED
const title = fixture.debugElement.query(By.css('h1'));
expect(title.nativeElement.textContent).toContain('Page title');

There is no denying that it looks much simpler and clearer in Spectator! You will find dozens of examples in the documentation: https://github.com/ngneat/spectator.

Installation

If I've convinced you of Spectator, there's nothing left to do but add it to your project along with ng-mocks via npm:

npm i -D @netbasal/spectator ng-mocks

Schematics

It is also worth mentioning that Spectator has its Schematics. For example, to generate a test file for a component, we can use the command:

ng g cs path/component-name

Bonus!

If you code using Visual Studio Code, be sure to add the snippet plugin:

Practice with a playground from the authors:

PLAYGROUND

Enjoy testing! ?

Share this post

Sign up for our newsletter

Stay up-to-date with the trends and be a part of a thriving community.