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
.
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 useelement.querySelector('img')
to query theHTMLElement
to check ifsrc
is equal tophotoUrl
.
- For the case without
photoUrl
, we can simply getelement.textContent
of theHTMLElement
to check if it is equal to the first initial ofitem.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.
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.
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:
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)
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 fromfx-layout
tofxLayout
). 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! š»