How to Use the Power of Jest’s Snapshot Testing Without Using Jest
The snapshot (aka Characterization or Golden Master) testing approach is nothing more than a diffable representation of the system’s state, so if an intentional or unintentional change occurs, the test fails and showing the difference of expected and actual output.
Because of that, snapshot testing is convenient to deal with things not directly related to business logic, but rather to representation (or state) of the system. It could be HTML, XML, JSON or any other applicable format.
Instead of writing numerous assertions it’s better to test manually (or say, visually) and if the state is right, freeze it (turn it to snapshot). All upcoming deviations would be considered as problems which would fail the test. You can then either fix the problem or confirm that the new behavior is right and approve current snapshot.
All snapshots are stored near to your code, under version control and became an integral part of implementation itself.
Not only React components
Even if snapshot based testing re-gained its popularity with React / Jest front-end framework, the application of the approach is much broader and can be applied in many other unusual cases.
We use snapshots to test HTTP API endpoints which typically responding JSON, but also HTML and XLSX formats.
Imagine that you can test complex reporting endpoint. Instead of,
You can do,
Cool, isn’t it? Very cool, but there is a small disclaimer. You have to take care about the format of a snapshot.
The format matters
The better response could be serialized for diffing, the better quality of tests you would have. It means, then the test fails you should have a clear understanding of what went wrong.
Frameworks like Jest and Enzyme taking care of that by serializing React components into JSON, well formatted for diffing.
Not only Jest
After the React 0.13 release, Facebook delivered a testing framework named Jest designed especially for React needs. With Jest, you have “batteries-included” set of tools, one of each is snapshot testing.
I don’t mind Jest, but there is a problem with it. It’s too opinionated. Which is useful for new projects, but what to do for an existing project with the already established setup?
In our case, we stick to Mocha and Michael Jackson’s Expect libraries as our tools of choice. The good thing, you can have Jest testing benefits, without actually using full framework.
Dissecting Jest, a little
With great advice from my colleague, Can Göktas I realized that design of Jest is pretty much composed of different packages. One of the packages is jest-snapshot
available as standalone npm package.
I would mention that architecture of Jest and re-usability of it’s individual components is a great job by Christoph Nakazawa and the team.
The package is rather small to understand details of its work. It exposes the function toMatchSnapshot()
which takes entity for snapshotting. It delegates the actual matching to another object, called snapshotState
and returns an object with information about did it pass or not as well as extended report.
After checking the tests for the package, I realized it’s not a big deal to use the package alone without using Jest itself.
So, I’ve came up with such function:
There is a function with such parameters actual
, testFile
and testTitle
which are not currently initialized, but we deal with them further.
Extending Expect
Now, we need to extend expect
to support toMatchSnapshot()
method. Expect is quite easily extendable via, expect.extend({})
method.
So, what we need to do is,
Depending on results returned by toMatchSnapshot
it will either fail or pass the assertion.
Here comes Mocha
It’s almost there, but as you can see from function above we are missing couple of variables testFile
, testTitle
, testFile
points to the path where the test file is, so SnapshotState
can create __snapshots__
folder near by. testTitle
is used as unique index in snapshot storage, so it can be easily extracted for comparison.
We can initialize both variables by asking Mocha. So, finally, I decided to put the Mocha instance as context for toMatchSnapshot()
of expect
.
Where makeTestTitle
is a simple (and quite ugly) function, that combines the name of all compound context.
So, in case of test like,
it would produce
”GET /v2/invoices/{id} specs de should return correct html”
good enough.
Finally, you can see I’m using beautify
function. As I mentioned above, the way you prepare a response for snapshots and diffing is very important to the quality of results. If an endpoint responds with ugly HTML you can understand nothing from a diff result. Beautifying the results will make it more diff friendly.
Conclusions
With the help of jest-snapshot
package, it’s quite easy to integrate snapshot testing to pretty much any setup. Whatever test or assertion framework you use.
I showed an example we approached the problem, of testing HTML resulting endpoint where snapshots are particularly shining. The full example of such tests you can find this gist.
My initial idea was to package the code to a separate npm
package, but then I realized it’s too much coupled with a particular framework such as Expect or Mocha.
Instead, I saw that it’s better to understand the way jest-snapshot
can be used independently, so you have a freedom to adopt it in any project you work.
Hope this helps!
Update
Apparently, I was too lazy to create a separate npm
package, but @Can Göktas fortunately is not that lazy as me. Please use it from here https://github.com/blogfoster/expect-mocha-snapshot.