Testing Angular components with @Input()
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!
This post will show multiple ways to test a component that binds an input via the @Input() decorator.
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.
In order 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 Angulars OnInit lifecycle hook, we can trigger our processInput() even before displaying anything. So lets 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
Ahh, 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 our component not just always displays “INPUT TEXT”? Lets 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.
- In order to test components that bind an input via the @Input() decorator, we can create a Host Component in our test to wrap our test component.
- For multiple test inputs, we can add a setter to our host component.
- For more that one input binding and even more control over our component under test we can grab it from within the host component with @ViewChild and pass it into our test directly.
Complete source can be found here: https://github.com/AikoPath/ComponentInputTest
Did you know you can clap more than once for an article? Go ahead and try it out a couple of times, the button is right there! (or just click and hold) :)