Angular x Jest Snapshot Testing 📸

Verify UI changes in your component without opening a browser.

Imagine that we have a component called <app-avatar></app-avatar> that accepts item as an input. The component can be displayed in two ways:

  • if item.photoUrl exists, show the image;
  • else, show the first initial of the item.name.
Two states of the same component — with image and without image

To test the functionality of this component, the simplest solution is to use Angular’s built-in DOM testing. The component fixture created through @angular/core/testing has a property called nativeElement that returns a HTML Element object. This object has an interface similar to how you would interact with an HTML Element in a browser. We can use this interface to query our target DOM elements and see if their contents are what we expect:

  • For the case with photoUrl, we can use element.querySelector('img') to query the HTMLElement to check if src is equal to photoUrl.
  • For the case without photoUrl, we can simply get element.textContent of the HTMLElement to check if it is equal to the first initial of item.name.

This kind of validation works perfectly for this basic use case. It would be better, however, if we can see how the rendered HTML changes between the two cases without building our app and opening a browser. We can achieve this by using Jest Snapshot Testing.

📸 Jest Snapshot Testing 📸

Jest Snapshot Testing is a perfect tool for testing whether your UI component renders properly without needing to open up a browser. I personally use this library to use Jest as testing library for any Angular Project.

According to the Jest’s documentation:

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.
A typical snapshot test case for a mobile app renders a UI component, takes a screenshot, then compares it to a reference image stored alongside the test. The test will fail if the two images do not match: either the change is unexpected, or the screenshot needs to be updated to the new version of the UI component.

A common misconception about “snapshot” though, could be that it takes a screenshot of the rendered HTML in the browser and returns an image of your component (This is also how I thought it worked before ¯\_(ツ)_/¯). Instead, Jest creates a .snap text file that shows how your HTML will be constructed. It is possible to create an image based on the snapshot that Jest generates but you’ll need to use another library.

To use snapshot testing, we need to update our tests to check the snapshots for validation instead of using DOM testing.

  • For the case with photoUrl:

Instead of getting the value from an HTMLElement we can simply assert if the component fixture matches its snapshot. Note that in the initial run of the test (when there aren’t any snapshots to test against), Jest will treat the first output as the main reference and will save the output in a __snapshots__ directory in the same directory as the test file.

All snapshots that will be validated against is saved in a __snapshots__ folder in the directory as the test file.

Here’s an example of the .snap file generated for a component that has a photoUrl. Note that the <img> tag exists with the desired src attribute.

Snapshot file for the component where photoUrl exists

Another great feature of snapshot testing is that when your snapshot has a mismatch, it shows you the difference in the rendered HTML. Here’s an example where there’s a mismatch in the snapshot:

Jest highlights mismatch in HTML when snapshots do not match.

In the same manner, we can change the test for the case when photoUrl does not exist.

  • For the case without photoUrl:

Similar to the other case, expect(fixture).toMatchSnapshot() generates a snapshot for this case and adds it to the .snap file. (Each test file corresponds to one .snap file. If there are multiple snapshot assertions in one test file, all these snapshots will be stored in the same .snap file)

Snapshot for the component that renders the first initial of the name instead of an image.

Some thoughts around using Snapshot Testing 🤔

  • Although the example shown above may be useful, not all HTML files should be snapshot tested. Make sure that you are using the most appropriate testing technique that best fits your use case.
  • Snapshot Testing is beneficial for very small files. However, when your HTML output has many lines of code, it will be challenging to figure out which part is being tested and could potentially lead to more confusion. Alternative techniques like DOM testing might be more useful in this case.
  • When there’s a third-party library that involved in rendering your HTML files (i.e. in my case, I use @angular/flex-layout for layouts), you might encounter false negatives when the third-party library that you’re using changes some syntax in their API. Although they may not be any functional changes, the syntax changes will cause your tests to break (i.e. the API changing from fx-layout to fxLayout). In some cases though, these might be good to catch 😅

That’s all! 👍

Hope this blog post has been beneficial in anyway. Got any questions/comments? @ me on twitter 😄

Happy coding! 💻