Skip to content
Brandon Domingue edited this page Aug 3, 2018 · 21 revisions

First, install shallow-render:

npm install -D shallow-render

That's it, you're be ready to write a test!


Your first test

Start by identifying the component you want to test, and the Angular module that component lives in.

For this example, let's say we have FooComponent it lives in the FooModule like so:

foo.component.ts

@Component({
  selector: 'foo',
  template: '<h1>{{label}}</h1>'
})
export class FooComponent {
  @Input() label = 'FOO!!';
}

foo.module.ts

@NgModule({
  declarations: [FooComponent],
  exports: [FooComponent]
})
export class FooModule {}

We want to write tests to cover the functionality of the FooComponent to make sure it renders the right thing.

foo.component.spec.ts

import { Shallow } from 'shallow-render';
import { FooComponent } from './foo.component';
import { FooModule } from './foo.module';

describe('FooComponent', () => {
  let shallow: Shallow<FooComponent>;

  beforeEach(() => {
     shallow = new Shallow(FooComponent, FooModule);
  });

  it('displays a default when no label is set', async () => {
    const {find} = await shallow.render(`<foo></foo>`);

    expect(find('h1').nativeElement.textContent).toBe('FOO!!');
  });

  it('displays provided label', async () => {
    const {find} = await shallow.render(`<foo label="My Label"></foo>`);

    expect(find('h1').nativeElement.textContent).toBe('My Label');
  });
});

Let's break this down starting with the shallow variable:

  let shallow: Shallow<FooComponent>;

This creates a closure that allows all of our specs access the shallow renderer.

Next, our beforeEach fires before each test and sets up a fresh shallow renderer for every test. This means every script gets a clean renderer with fresh mocks.

  beforeEach(() => {
     shallow = new Shallow(FooComponent, FooModule);
  });

Notice, we pass two items into the Shallow constructor, first, the component we wish to test, in this case the FooComponent. Next, we pass in the module that our test component lives in (the NgModule we declare our component to be a part of). We pass in the module because our Angular module should provide all the dependencies our component needs to render itself including providers, pipes, directives and other components that our component may need. Shallow will mock any child providers and components so they stay out of our way while testing the FooComponent.

Now let's look at one of the tests, take note of a few things:

  it('displays provided label', async () => {
    const {find} = await shallow.render(`<foo label="My Label"></foo>`);

    expect(find('h1').nativeElement.textContent).toBe('My Label');
  });

We use async functions.

  async () => {

When we render with Shallow, part of the process involves compiling templates with TestBed. This is an asynchronous process, so we use the async keyword to allow us to use the easier-to-read syntax when dealing with promises.

Then we render our component:

  const {find} = await shallow.render(`<foo label="My Label"></foo>`);

When we render with Shallow, we use the exact same template HTML style that we would use when we write Angular HTML. You can even bind variables and events if you want. Notice we're setting the label input to My Label in the test.

If the const {find} looks weird, it's another bit of ES6 syntactic sugar called destructuring. It's not necessary to destructure, but I like it so you can do it with Shallow.

Once we're done rendering, we make our assertion:

  expect(find('h1').nativeElement.textContent).toBe('My Label');

This is a pretty standard expectation, just make sure that our FooComponent accepted and rendered our label value properly.

That's it! There are lots of examples for all kinds of scenarios.

Pro Tips

You aren't required to use template HTML to render your components if you don't want to!

For example, the sample above:

    const {find} = await shallow.render(`<foo label="My Label"></foo>`);

could be simplified as:

    const {find} = await shallow.render({bind: {label: 'My Label'}});

In this case, Shallow will force the bind properties to match the names and types of the inputs for your component. If you want to use HTML templates, that's cool too. This is just an type-safe alternative.

Clone this wiki locally