Angular Unit Testing @ViewChild

How to Unit Test @ViewChild using a Stub Component

Todd Palmer
Angular In Depth
7 min readMar 13, 2019

--

A View of Children, unit testing some Components at Nallikari Beach in Oulu, Finland

AngularInDepth is moving away from Medium. This article, its updates and more recent articles are hosted on the new platform inDepth.dev

If you have been using Angular for any length of time, you have probably run into a parent component that references a child component using @ViewChild. Unit testing the parent component is trivial, if you just want to include the child component in the test. However, it gets tricky when we want to mock the child component with a stub.

In this article we will discuss:

  • A simple introduction to using @ViewChild
  • Unit testing your parent component without mocking the child
  • Unit testing your parent component with a stub child component

To get the most out of this article, you should have at least an introductory understanding of Angular Unit Testing.

Introducing @ViewChild

If you are already familiar with @ViewChild and how it is used, feel free to skip this section.

Simply put, @ViewChild lets us make calls to the functions on a child component. Instead of only using the component’s @Input(), we can treat the component more like an API and access its public interface.

Example child component

Take a look at this simple component:

Notice that ChildComponent just displays the current timestamp when it loads. It also has a public function updateTimeStamp() that causes the timestamp to refresh.

Example parent component

Now let’s assume we have a parent component that wants to display ChildComponent and also wants to be able to tell it to update the displayed timestamp. We can do something like this:

As you can see, ParentComponent shows a button with text: Update. Clicking this button calls the update() function in ParentComponent which in turn calls the updateTimeStamp() function on ChildComponent.

The line that gives ParentComponent access to the instance of ChildComponent is:
@ViewChild(ChildComponent) childComponent: ChildComponent;

Here is a working example on StackBlitz:

Testing @ViewChild with a Component

Now we want to write a simple unit test for ParentComponent. To keep things simple, we won’t worry about testing the button’s click event handler. We will just test that the update() function in ParentComponent calls the updateTimeStamp() function in the ChildComponent.

Our parent.component.spec.ts file looks like this:

Notice that our ParentComponent unit tests reference ChildComponent in the declarations on line 13.

In lines 30–32 we are just testing that the update() function calls the updateTimeStamp() function in ChildComponent.

We can run our unit tests using:

$ npm run test

And we see that all of our unit tests pass:

So far so good.

Testing @ViewChild with a Stub Component

Testing our ParentComponent by just including ChildComponent in the declarations isn’t optimal. This is because problems in ChildComponent will cause problems in our ParentComponent tests. For unit testing we would really prefer to isolate our component from its children.

Typically, we mock out our component’s dependencies by creating stubs for the child components. Here is an example of mocking ChildComponent with a simple stub:

Hopefully, you have used stub components like this before. But, for clarity I will quickly explain how it works. Notice that the stub is just a minimal implementation of the real thing:

  • We use app-child for the selector which is the same as the real component.
  • The template is just an empty string.
  • It has a very simple implementation of the updateTimeStamp() function because it is invoked by our test.

Testing with the stub

Now let’s try to use this stub component in our unit test for ParentComponent. In our declarations we can just replace ChildComponent with ChildStubComponent. For example, the asynchronous beforeEach() function in parent.component.spec.ts should now look like this:

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
ParentComponent,
ChildStubComponent
]
})
.compileComponents();
}));

And now when we run our unit tests with:

$ npm run test

It fails. Argh!

Providing the @ViewChild for Testing

Hmm, so the unit test can’t find the instance of ChildStubComponent. OK, we will need to somehow provide the @ViewChild for the test component.

There are actually a couple of ways to do this:

  • Manually populate the child component in our unit test
  • Add a provider in the stub child component

Of these, adding the provider is my preferred way. But, because this is Angular In Depth, and not Angular the Way Todd Thinks You Should Do It, we will look at both ways and I will let you be the judge.

Manually populating the child component

We will change our synchronous beforeEach() function like this:

beforeEach(() => {
fixture = TestBed.createComponent(ParentComponent);
component = fixture.componentInstance;
// populate childComponent with an instance of the stub
component.childComponent =
TestBed.createComponent(ChildStubComponent).componentInstance;
fixture.detectChanges();
});

As you can see, we are using:
TestBed.createComponent(ChildStubComponent).componentInstance;
to create an instance of the stub component.

OK, let’s run those unit tests again:
$ npm run test

ERROR in src/app/parent/parent.component.spec.ts(23,5): error TS2739: Type 'ChildStubComponent' is missing the following properties from type 'ChildComponent': timeStamp, ngOnInit

Compile error! Are you kidding me?

Matching the type of ChildComponent

We get the compile error because TypeScript is rather strict about its type checking (hence the name). Even though timeStamp and ngOnInit() aren’t necessary for the testing, they are part of the public interface of ChildComponent and TypeScript expects them to be there.

We could just add the missing ngOnInit() and timeStamp members to our stub component. But, it is easier and more fun to cast our ChildStubComponent to a ChildComponent.

Casting the component

TypeScript, like most strongly typed languages, provides the ability to type cast objects. There are a couple of ways to do this in TypeScript.

You can wrap the cast in <>. For example:
const myFoo: Foo = <Foo> bar;

You can also use the as keyword like this:
const myFoo: Foo = bar as Foo;

My tslint prefers the as keyword. As my friend Tim Deschryver pointed out, this is because of this line:
"no-angle-bracket-type-assertion": true,
in my tslint.json file.

Making the change in the parent.component.spec.ts file looks like this:

And our unit tests compile and run.

Adding a provider to the stub component

My preferred way to handle this is to specify a provider in the stub component. Alexander Poshtaruk came up with the idea for this. And, thekiba actually showed us exactly how to do it.

You have probably seen and even used components that look like this:

@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [
{
provide: HeroService,
useClass: BetterHeroService
}
]
})
export class HeroListComponent {
/* . . . */
}

You’ll notice that this component has its own providers array in the metadata. In the providers array there is an object that looks like this:
{ provide: HeroService, useClass: BetterHeroService }
This object is known as a Dependency Provider. The useClass is the provider-definition key. If you have ever mocked services for your unit tests, you have probably used this.

Actually, thekiba recommended useExisting. But, I didn’t want to have to explain the difference here. And, this article is already getting too long and detailed. And, I might want to use that topic for a possible future article. And, I am afraid that you will stop following me if I dive any further down this rabbit hole.

OK, back to the providers array:

As I was saying before I so rudely interrupted myself: you usually put services in the providers array. But surprisingly, you can also put dependency providers for components in there.

Let’s modify ChildStubComponent to use a useClass provider for ChildComponent. It looks like this:

Here, we are telling Angular that if this class is part of the build, it should provide ChildStubComponent for ChildComponent. Because this code is in a .spec file, it will only take effect during testing.

Finally, I just want to remind you that when we use the provider, we do not need to populate the child component instance manually anymore with TestBed.createComponent.

Run the Unit Tests

Now when we run the tests using:

$ npm run test

We see that our unit tests all compile and pass:

Yes!!!!

Summary

So, to test a component that uses @ViewChild just do the following:

  1. Mock out the child component with a simple stub component.
  2. Add the stub component to the declarations in your parent component .spec file.
  3. Add a useClass Dependency Provider in the stub component’s providers array like this:
    { provide: HeroService, useClass: BetterHeroService }

Resources

I created a GitHub repo that contains all the code for this article:

Shameless plugs of other articles

My cohorts on Angular In Depth and I have written some articles about Angular Unit Testing:

You will want to confirm your understanding of the how and when of beforeEach() with:
How I Was Completely Wrong About Setting Up/Tearing Down Tests!
by Zack DeRose.

If you need to test with an NgRx store, check out:
How To Unit Test Angular Components With Fake NgRx TestStore
by Tomas Trajan.

My friend Lars Gyrup Brink Nielsen wrote about testing Container Components:
Testing Angular Container Components

For an introduction to using Headless Chrome you can follow along with my own article:
Angular Testing with Headless Chrome

Finally, take a look at Tim Deschryver’s excellent article:
How I test my NgRx selectors

--

--

Todd Palmer
Angular In Depth

Computer Scientist, Fujitsu Distinguished Engineer, and Senior Software Engineer http://t-palmer.github.io