序章

スナップショットテストは、コードのリグレッションを監視し、統合テストとしても機能するJestのテストの一種です。 1つ目は、プロジェクトにコードを追加し、何か小さな問題が発生した場合、スナップショットテストでそれを検出できることを意味します。 2つ目は、スナップショットテストが、コンポーネント全体が意図したとおりに実行されることを確認する方法であることを意味します。

スナップショットテストの仕組みは、jestを初めて実行するときに、DOMのスナップショットが生成されることです。 その後のテストスイートの実行時に、構築されたDOMがこれらのスナップショットと比較されます。 コードを変更した可能性があるため、最初に生成されたものと一致するスナップショットは、物事がまだ機能していることを示しています。

いくつかの質問が自然に出てきます。プログラムに大幅な変更を加えて、DOMの内容が異なる場合はどうなりますか? Jestを使用すると、新しいスナップショットを生成できます。そのようなシナリオでは、それが保証されます。 私のページに非決定論的なコンテンツがある場合はどうなりますか? これを処理する方法は複数ありますが、これについてはすぐに説明します。

アプリのセットアップ

次に、アプリケーションをセットアップします。 テスト用の簡単なアプリをセットアップするために、Jestを使用したVueのテストに関するチュートリアルのセットアップセクションに進んでください。 App.vueファイルは次のようになります。

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>{{ x1 }} + {{ x2 }} =</p> <input v-model="guess"> <button v-on:click="check">Check Answer</button>
      </div>
      <button v-on:click="refresh">Refresh</button>
      <p>{{message}}</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>

そして、これが私たちが始めたapp.spec.jsがどのように見えるかです:

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!");
  });
});

itは、Jestのtestの単なるエイリアスであることに注意してください。 npm run testを実行すると、すべてのテストに合格するはずです。

スナップショットテストを始めましょう!

npm install --save-dev jest-serializer-vueを実行し、package.jsonに以下を追加します。

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

2番目のdescribeブロックにコードを追加します。

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

テストを実行し、最初にテストを実行したときに「1つのスナップショットが書き込まれた」と表示されることに注意してください。 app.spec.jsの隣に__snapshots__というディレクトリが作成されていることに注意してください。

.snap拡張子で終わるファイルを持つスナップショットファイルを自由に見てください。 接頭辞v-が付いている属性を除いて、コンポーネントのテンプレートセクション全体が再現されていることがわかります。

テストを再実行してください。

エラー!

なぜ?!

ターミナルでスナップショットテストの出力を調べると、理由は明らかです。ページにランダムに数値が生成されています。 また、スナップショットの番号が何であるかを確認できるはずです。 コンポーネントをマウントするときにdataに合格して、テストに置き換えてください。 渡した関数は、コンポーネント自体のdataにマージされます。

もう一度通過すると、次のようになります。

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

もう1つのアプローチは、コード内の非決定論的関数のモック関数を作成することです。 私たちの場合、それはMath.random()です。

あなたは次のようなものになってしまうでしょう:

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

ヘッダーをページの写真の上に移動したいとします。 これはVueコンポーネントへの簡単な変更なので、先に進んでそれを行ってください。 テストスイートをもう一度実行してみてください。

エラー!

スナップショットのページの配置が異なるため、失敗しました。 スナップショットのその部分を更新する必要があり、npm test -- -uを実行することで更新できます。

これで、テストは再び合格しました。

成功!

スナップショットをインタラクティブに更新する場合は、npm test -- -iを実行できます。

結論

スナップショットは、アプリケーションのインターフェースへの偶発的な変更に遅れないようにするのに非常に役立ちます。 スナップショットは、他のコードと同様にGitにチェックインする必要があります。 テストが失敗した場合は、スナップショットを再帰的に更新する前に、何が起こったかを確認してください。

スナップショットテストは、特に複雑になるため、Vueアプリケーションのテストに非常に役立ちます。 頑張ってください!