Quick guide: test an Angular child component prop

Quick guide: test an Angular child component prop

So we've all been there: you have a child component in your component and you wish to check whether it's receiving the right Inputs, for example. Any way, you want the child component to do stuff with.

So for this example, I have created a component called `MyButtonComponent` using `$ ng generate component my-button`. MyButtonComponent has two @Inputs: `label` and `type`.

The goal is to test whether these two @Inputs have been set correctly in our AppComponent.

Now we could use `@ViewChild` in the component like so:

No alt text provided for this image

And then in the tests, we'll simply use the field to check the label.

No alt text provided for this image

However, this is bad form

The reason it's bad form is that the `buttonRef` is set in the component, but it's not because the component needs that, but only the tests need it. That means that the component is polluted with code that isn't needed and we probably shouldn't use this solution. This is especially going to show when you have a bigger container component and need to maintain a list of every child.

The proper solution

We can use code that's only defined in the tests to find the MyButtonComponent and perform tests on it. To do that, first we get the DebugElement. This is a kind of 'wrapper' around an HTML element which has some more options.

Let's get the DebugElement by writing:

const buttonDebugEl = fixture.debugElement.query(By.directive(MyButtonComponent));
        

(an alternative way to get this element is by using a css selector. A quick demo you can find at the end of this article).

However, if we now try to access `buttonDebugEl.label`, we will get an error. We can't use `buttonDebugEl.nativeElement.label` either, because `label` is an Angular binding and no ordinary attribute like `class`, `id` or `title`.

So we need to access the component, and luckily for us, that's pretty easy. There's an `injector` which allows us to get any Directive or Component that is 'manipulating' our DebugElement. That's a pretty long intro to say you just need to type this:

// First get the DebugElement
const buttonDebugEl = fixture.debugElement.query(By.directive(MyButtonComponent));

// Then get the component
const buttonComponent = buttonDebugEl.injector.get(MyButtonComponent);

// And NOW we can access the fields!
expect(buttonComponent.label).toContain('Great'); // will work as we expect        

Of course, you can shorten this if you like, but bear in mind that, if you need the HTML element, you can only access it with `buttonDebugEl.nativeElement`.

Alternative way of getting the debugElement: using a css selector

Alternatively, you can query by a css selector using something like:

?const buttonDebugEl = fixture.debugElement.query(
? ? ?By.css(`[data-testid="button"]`)
?);        

where the app-my-button-element of AppComponent has the `data-testid` attribute set to `button`:

<app-my-button label="Great label"
? ? ? ? ? ? ? ?type="submit"
? ? ? ? ? ? ? ?data-testid="button"></app-my-button>"        

This has an advantage because you can target the exact button you want to test.

Check out the Stackblitz demo, and if it's not clear or you simply like the article, leave a reaction! ??

Tags: angular, viewchild, child field, child input, get child from parent

要查看或添加评论,请登录

Aart den Braber的更多文章

社区洞察

其他会员也浏览了