Testing Angular Components With @Input()

Multiple ways of testing a component that binds an input via the @Input() decorator

When developing an Angular (read Angular 2 or Angular 4, or whatever the current version is when you read this) component that takes an input, you might decide to unit test the whole component. At least I hope you do!

For example, we have a component, ComponentUnderTest , in which we want to display upcased input… I know right: what a great use-case, every business owner needs that!

So, let’s say the ComponentUnderTest binds and displays input.

To verify that processInput() correctly upcases our input, we can simply assign a test value to the input variable and assert that, after calling the method, the displayed input is in ALL CAPS.

Easy peasy! Right?

But by now, we need to manually call processInput() and until we call it, our input is still displayed in lousy lowercase.

Luckily, with Angular’s OnInit lifecycle hook, we can trigger our processInput() even before displaying anything. So let’s implement it and call processInput() in the corresponding method.

Let’s run the tests!

Oh no! The tests fail!

Cannot read property ‘toUpperCase’ of undefined

Ah, of course, by the time toUpperCase() is called on the input in our ngOnInit() we have not even assigned any value to it yet.

When we actually embed our component in the production code, we would have set the value of input by binding the input in the component’s tag.

So let’s embed the ComponentUnderTest in a TestHostComponent . By actually having a host or parent component, we are able to pass in exactly the input we want for our test.

In our test, we can simply define this TestHostComponent , include it in the testing module configuration, and instantiate it in our beforeEach method.

What results are green tests and the input is upcased at the beginning.

However, can we really be sure that the input gets upcased and that our component not just always displays “INPUT TEXT”? Let’s write another test that upcases “different test input”.

What we could do to achieve this is define another TestHostComponent which binds another input but we can do better than that!

setInput() sets the input in our host component. Assigning the input in our test before we let Angular detect the changes allows both our tests to pass..

Now, imagine we have to bind not only one but multiple inputs to our component. Do we now need to write a setter method for each input?

Luckily, the answer is no!

Instead of having a setter at all, we can also get a reference to our component from within the host component and pass it into our test. There we have full control over our component and can modify any field we want.