avatarAli Kamalizade

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

1834

Abstract

y:</p><ul><li>Have few or no internal states.</li><li>Declare <a href="https://angular.io/api/core/Input"><code>In</code>put</a> properties to allow their parent element to provide data.</li><li>Communicate with their parent element with the help of <a href="https://angular.io/api/core/Output"><code>Out</code>put</a> properties.</li></ul><p id="5634">Sadly, components that use the <code>OnPush</code> 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 <a href="https://angular.io/api/core/testing/ComponentFixture#detectchanges"><code>fixture.detectChange</code>s()</a>for the first time. There are popular issues on GitHub (<a href="https://github.com/angular/angular/issues/12313">example</a>) that highlight the severity of this problem. Let’s have a look at some ideas to solve this problem:</p><ul><li>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.</li><li>You can wrap your component in another component. As long as only <code>Input</code> properties 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.</li></ul><p id="5513">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.</p><h1 id="2fc9">How to Trigger Change Detection for OnPush Components i

Options

n Tests</h1><p id="dfd3">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:</p> <figure id="b6b9"> <div> <div>

            <iframe class="gist-iframe" src="/gist/ali-kamalizade/14f7f0ab19f6592adf2f05cd6215dabf.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
          </div>
        </div>
    </figure></iframe></div></div></figure><p id="7be0">Whenever you need to run change detection for a component using the <code>OnPush</code> change detection strategy, you can call this function (make sure to use <code>await</code> since it returns a <code>Promise</code>) and your changes should be picked up. The only argument you need to provide is a <code>ComponentFixture</code>, which you typically get when creating your component with <a href="https://angular.io/api/core/testing/TestBed"><code>Test</code>Bed</a>.</p><h1 id="837f">Conclusion</h1><p id="ae8d">Thanks for reading this article. Using the <code>OnPush</code> 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 <code>OnPush</code> components. I hope that the Angular team will make change detection for <code>OnPush</code> components easier in the future without having to resort to these kinds of workarounds.</p><p id="73e1">Do you know other ways to reliably detect changes in unit tests for <code>OnPush</code> components? Let me know in the comments.</p></article></body>

How to Write Tests for Components With OnPush Change Detection in Angular

Making Angular aware of changes in component tests

Photo by Ross Findon on Unsplash.

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:

  • Default uses the default CheckAlways strategy in which change detection is automatic until explicitly deactivated. If you don’t specify a strategy, then this will be used.
  • OnPush uses the CheckOnce strategy, meaning that automatic change detection is deactivated until reactivated by setting the strategy to Default. Change detection can still be explicitly invoked by using ChangeDetectorRef.

The OnPush change detection strategy is a good choice for dumb components that typically:

  • Have few or no internal states.
  • Declare Input properties to allow their parent element to provide data.
  • Communicate with their parent element with the help of Output properties.

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 Input properties 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 TestBed.

Conclusion

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.

Programming
Angular
Software Engineering
JavaScript
Testing
Recommended from ReadMedium