Introduction to Jest Snapshot Testing in Vue.js

Parthiv Mohan

Snapshot testing is a type of testing in Jest which monitors regression in your code and also serves as an integration test. The first means that if you add more code to your project and something small breaks, snapshot testing can catch it. The second means that snapshot testing is a way of making sure an entire component runs the way you intend it to.

The way snapshot testing works is that the very first time you run jest, snapshots are generated of the DOM. On subsequent runs of your test suite, the DOM that’s constructed gets compared to these snapshots. Since you may have changed your code, your snapshots still matching the ones generated the first time tells you things are still working.

Some questions naturally come up: what if I make a significant change to your program which results in different DOM contents? Jest allows you to generate new snapshots and such a scenario would warrant that. What if there is non-deterministic content on my page? There are multiple ways to handle this, and we’ll see this shortly!

App Setup

We’ll now setup our application. Head over to the setup section of our tutorial on testing Vue using Jest for setting up a simple app for testing. Here’s what your App.vue file could look like:

App.vue

<template>
  <div id="app">
    <div>
      <h3>Let us test your arithmetic.</h3>
      <p>What is the sum of the two numbers?</p>
      <div class="inline">
        <p> +  =</p> <input v-model="guess"> <button v-on:click="check">Check Answer</button>
      </div>
      <button v-on:click="refresh">Refresh</button>
      <p></p>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      x1: Math.ceil(Math.random() * 100), 
      x2: Math.ceil(Math.random() * 100),
      guess: "",
      message: ""
    }
  },
  methods: {
    check() {
      if (this.x1 + this.x2 === parseInt(this.guess)) {
        this.message = "SUCCESS!"
      } else {
        this.message = "TRY AGAIN"
      }
    },
    refresh() {
      this.x1 = Math.ceil(Math.random() * 100);
      this.x2 = Math.ceil(Math.random() * 100);
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.inline * {
  display: inline-block;
}
img {
  height: 350px;
}
</style>

And here’s what our started app.spec.js looks like:

import { mount } from "@vue/test-utils";
import App from "./../src/App.vue";

describe("App", () => {
  // Inspect the raw component options
  it("has data", () => {
    expect(typeof App.data).toBe("function");
  });
});

describe("Mounted App", () => {
  const wrapper = mount(App);

  test("is a Vue instance", () => {
    expect(wrapper.isVueInstance()).toBeTruthy();
  });

  it("renders the correct markup", () => {
    expect(wrapper.html()).toContain(
      "<p>What is the sum of the two numbers?</p>"
    );
  });

  // it's also easy to check for the existence of elements
  it("has a buttons", () => {
    expect(wrapper.contains("button")).toBe(true);
  });

  it("renders correctly with different data", async () => {
    wrapper.setData({ x1: 5, x2: 10 });
    await wrapper.vm.$nextTick();
    expect(wrapper.text()).toContain("10");
  });

  it("button click without correct sum", () => {
    expect(wrapper.vm.message).toBe("");
    const button = wrapper.find("button");
    button.trigger("click");
    expect(wrapper.vm.message).toBe("TRY AGAIN");
  });

  it("button click with correct sum", () => {
    wrapper.setData({ guess: "15" });
    const button = wrapper.find("button");
    button.trigger("click");
    expect(wrapper.vm.message).toBe("SUCCESS!");
  });
});

Keep in mind that it is just an alias for test in Jest. Run npm run test and all tests should pass.

Let’s Start Snapshot Testing!

Run npm install --save-dev jest-serializer-vue then make the below addition to package.json

package.json

{
  ...
  "jest": {
    "snapshotSerializers": ["jest-serializer-vue"]
  },
  ...
}

Add some code to the second describe block.

it('renders correctly', () => {
  const wrapper = mount(App)
  expect(wrapper.element).toMatchSnapshot()
})

Run your tests and notice how the first time you run your tests you should see “1 snapshot written”. Notice that a directory called __snapshots__ has been created next to app.spec.js.

Feel free to take a look at the snapshot file, which has a file ending with the .snap extension; you'll notice that the whole template section of the component has been reproduced, except for attributes that have the prefix v-.

Run your tests again.

Error!

Why?!

If you examine the snapshot test’s output in your Terminal, it’s clear why: we have randomly generated numbers on our page. You should also be able to see what the numbers in the snapshot are. Go ahead and substitute those into your test by passing data when you mount your component; the function you pass will be merged into the component’s own data.

It should look something like this once you have it passing again:

it('renders correctly', () => {
  const wrapper = mount(App, {
    data() {
      return {
        x1: 37,
        x2: 99
      }
    }
  })    
  expect(wrapper.element).toMatchSnapshot()
})

Another approach is to write a mock function for the nondeterministic function in our code. In our case that is Math.random().

You’d wind up with the something like the following:

it('renders correctly with mock', () => {
  Math.random = jest.fn(() => .37);
  const wrapper = mount(App)    
  expect(wrapper.element).toMatchSnapshot()
})

Let’s say we wanted to move our header to be above the photo on the page. This is an easy modification to your Vue component so go ahead and do that. Try running your test suite again.

Error!

It failed because our snapshot has the page arranged differently. We must update that part of our snapshot and we can do that by running npm test -- -u.

Now our tests pass again.

Success!

If you wanted to update snapshots interactively, you can run npm test -- -i.

Conclusion

Snapshots can be very useful in keeping abreast of any accidental changes to your application’s interface. Snapshots should be checked into Git like any other code. If tests fail, check what has happened before reflexively updating your snapshots.

Snapshot testing should be very useful to you in testing your Vue applications, especially as they get more complex. Good luck out there!

  Tweet It

🕵 Search Results

🔎 Searching...