How to Write Tests for Components With OnPush Change Detection in Angular
The Angular framework does a lot under the hood to detect changes and update the UI accordingly. Similarly to other frameworks like React or Vue.js, Angular supports data binding to always show the latest data.
While Angular is already a fast framework, we can always improve things. One improvement idea is to reduce the amount of change detection work to keep the UI as smooth as possible. For Angular components, there are two change detection strategies available:
Defaultuses the default
CheckAlwaysstrategy in which change detection is automatic until explicitly deactivated. If you don’t specify a strategy, then this will be used.
CheckOncestrategy, meaning that automatic change detection is deactivated until reactivated by setting the strategy to
Default. Change detection can still be explicitly invoked by using
OnPush change detection strategy is a good choice for dumb components that typically:
- Have few or no internal states.
Inputproperties to allow their parent element to provide data.
- Communicate with their parent element with the help of
Sadly, components that use the
OnPush change detection strategy are harder to test in unit tests. The problem is that such a component will run change detection only once when calling
fixture.detectChanges()for the first time. There are popular issues on GitHub (example) that highlight the severity of this problem. Let’s have a look at some ideas to solve this problem:
- You can try to write your test code in such a manner that you only need to trigger change detection once. However, this can be cumbersome and some tests need multiple change detection runs.
- You can wrap your component in another component. As long as only
Inputproperties of the component provided by the wrapper component are changed, Angular should pick up these changes. For this approach, you need to create a wrapper component just to not have to deal with the change detection issue.
These approaches can work, but they seem to involve more effort than necessary in my opinion. One advantage of open source projects is that even if there’s no officially supported way, the community will often figure out a way. Now, I want to share with you what the community figured out to attempt to solve this problem.
How to Trigger Change Detection for OnPush Components in Tests
Not satisfied with having to deal with this problem, I tried out multiple ways to trigger change detection for such components in tests. Looking for a solution, I stumbled upon a neat function that I’d like to share with you:
Whenever you need to run change detection for a component using the
OnPush change detection strategy, you can call this function (make sure to use
await since it returns a
Promise) and your changes should be picked up. The only argument you need to provide is a
ComponentFixture, which you typically get when creating your component with
Thanks for reading this article. Using the
OnPush change detection strategy is a good way to improve performance by reducing the amount of change detection in your Angular application. By using a simple helper function outlined above, we can easily trigger change detection as often as we like in component tests — even for
OnPush components. I hope that the Angular team will make change detection for
OnPush components easier in the future without having to resort to these kinds of workarounds.
Do you know other ways to reliably detect changes in unit tests for
OnPush components? Let me know in the comments.