Today’s tip comes from no one else than Eduardo San Martín! You probably know him already since he’s one of the main core Vue.js team members.

We’re meeting today in Alicante and in a few days we’ll be speaking at the Vue Day. And surprise… We’ll have streaming! Make sure to follow @VueDay to get the link to the streaming and the recorded talks as soon as we publish them.

Alex Eduardo

Let’s get this tip going!


Have you ever heard about Snapshots being evil? About how fragile they are and how you should avoid them? It’s true! You should be extremely careful about them because they do exact comparison of content as text, that is if you are snapshoting a component, you are actually snapshoting its HTML content, so anything changing the HTML will break the snapshot and if this is repeated too often, you may end up accidentally accepting snapshots updates and missing regressions in your application 😱.

But you don’t have to snapshot the whole HTML! You can even provide a hint to recognize the snapshot and this can be used to generate tests fixtures on the flight in a very convening way, specially for very large content sets

Imagine you have a very big table and you want to test that given some props, the table renders the right content:

<table>
  <thead>
    <tr>
      <th v-for="column in columns">{{ column.name }}</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="item in items">
      <td v-for="column in columns">
        <span class="label">{{ colum.name }}: </span>
        <span class="value">{{ item[colum.key] }}</span>
      </td>
    </tr>
  </tbody>
</table>

Here columns is an array of all the columns in the table and items in an array of all the rows displayed. You could say both are props. If you want to test the content of the table given those props, you will have to test each row:

test('contains the right information', () => {
  // columns and items are defined above
  const wrapper = shallowMount(MyTable { props: { columns, items }})
  // first cell in the header
  expect(wrapper.find('thead th:nth-of-type(1)').text()).toBe('Product')
  // first row in the tbody
  expect(wrapper.find('tbody tr:nth-of-type(1) .value').text()).toBe('Dinner plates set of 8')
  // repeat for EVERY row 🤯
})

There are multiple approaches to select the table cell like using a data-test attribute but that’s not the issue here. Can we go faster when writing this kind of test? What if we wrote the component, check manually and then add a test that snapshots the current state?

test('contains the right information', () => {
  // columns and items are defined above
  const wrapper = shallowMount(MyTable { props: { columns, items }})

  const cells = wrapper.findAll("td");
  for (let i = 0; i < cells.length; ++i) {
    const cell = cells.at(i);
    // use label as the hint for the snapshot
    const label = cell.find(".label");
    if (!label.exists()) continue;    // filter out cells that do not have a label
    expect(cell.find(".value").text()).toMatchSnapshot(label.text());
  }
})

Writing this test will generate a snapshot the first time it is run:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MyTable contains the right information: Product Name 1`] = `"Dinner plates set of 8"`;

exports[`MyTable contains the right information: Sells 1`] = `"23"`;

exports[`MyTable contains the right information: Stock 1`] = `"3"`;

// more and more cells

The advantages of this solution is that adding a new column will create a single new snapshots without invalidating the others while removing existing columns will make some snapshot obsoletes and changing any of the cells .value content will make the snapshot test fail.

If you don’t like the idea of creating dozens of snapshots like this, you can create some custom text value and create one single snapshot:

test('contains the right information', () => {
  // columns and items are defined above
  const wrapper = shallowMount(MyTable { props: { columns, items }})

  const cells = wrapper.findAll("td");
  let content = ''
  for (let i = 0; i < cells.length; ++i) {
    const cell = cells.at(i);
    // use label as the hint for the snapshot
    const label = cell.find(".label");
    if (!label.exists()) continue; // filter out cells that do not have a label
    content += `${label.text()}: ${cell.find(".value").text()} \n`
  }

  expect(content).toMatchSnapshot()
})

You will end up with one single snapshot:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MyTable contains the right information 1`] = `
Product Name: Dinner plates set of 8
Sells: 23
Stock: 3
etc.
`;

So remember: Snapshots can also be used to generate tests fixtures with text!

Happy Testing!

Start saving time and get a tip about the Vue ecosystem every week, right in your inbox.

    Latest Vue Jobs