WEBその他

①Vue3×Jest×でTDD開発をする【RedからGreenまで】

vue WEB
この記事は約9分で読めます。

フロントエンドエンジニアの皆さん、テストは好きですか?
「フロント側のテストって、マニュアルのが早くね?」とか思っていませんか?
マニュアルで進めた方が早いかもですが、それは最初だけの話です。
もし、クライアントから

「昔と見た目とかが結構違うので、もう一回全部テストしてもらって良いですか?」

などと言われたら、だいぶ疲れませんか?少なくとも私は疲れてしまいます。
もっと楽にいきたいですよね。

例えば、コマンド一発でそれらが全部確認できるようなモノ、欲しいですよね。

そういうわけで、この記事は Vue3 × TDD (テスト駆動開発)です。
テストをしていくことで、堅牢なアプリケーションを作っていきましょう。

記事の内容
1:テスト駆動開発とは
2:環境構築(Vue3 x Jest)
3:Red → Green の順番で実装を進める(この記事はここまで)
次回:Red → Green → Refactoring

今回は、ユーザー登録フォーム部分をTDDで進めていきます。

1:テスト駆動開発とは
テスト駆動開発(Test-Driven Development: TDD)とは、テストファーストなプログラムの開発手法です。つまり、プログラムの実装前にテストコードを書き(テストファースト)、そのテストコードに適合するように実装とリファクタリングを進めていく方法を指します。
(引用 Qbook – テスト駆動開発(TDD)とは?TDDの進め方をステップ毎に解説!) https://www.qbook.jp/column/20181009_713.html

「なんでテストファーストなの?」
→実装してからのテストでは、「受かるテスト」を書いてしまうから。
また、「異常系」のテストを書いたときにエラーが出て、モチベーションが下がるかもしれません。
時間が無いからといって、テストの内容を変えても良いですが、本末転倒に変わりはないのです。
テストを先に書くことで、コレらの障壁をできる限り、低くします。

また、テスト容易性が高いものから進めることを勧めてます。僕よりも「テスト駆動開発」の著者が伝えている動画の方がよほど説得力があるので、リンクを貼ります。
https://www.youtube.com/watch?v=Q-FJ3XmFlT8

2:環境構築(Vue3 x Jest)vue cliを使用します。
https://cli.vuejs.org/

npm i -g @vue/cli

パスが通らないので、私は

export PATH=$PATH:/users/XXXXX/.npm-global/bin

というふうにして、パスを通しました。

vue create app
Vue CLI v4.5.15?
Please pick a preset
: Default ([Vue 2] babel, eslint) 
❯ Default (Vue 3) ([Vue 3] babel, eslint) 
Manually select features


Default(Vue3)を選びます。

vue --version
// @vue/cli xxx.xxx

実行して、バージョン表示確認後、

vue create app
cd app
yarn serve

で、ローカル環境が立ち上がるか確認します。

vue


立ち上がりました。

npm i -D jest@26.6.3 @testing-library/vue@next vue-jest@next

をして、再度、yarn serve後、もし vue-loader が無いと起こられたら、こちらを実行します。

npm i -D vue-loader-v16

package.jsonの変更をします。envに次のような追加をしてください。

"env": {
  "node": true,
  "jest": true,
},

とすることで、package.json に jest の構成を保存させます。https://jestjs.io/ja/docs/26.x/configuration

yarn test ができるように、package.jsonのscriptsに次のような追加をしてください。

"scripts": {
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "lint": "vue-cli-service lint",
  "test": "jest --watch"
},

と、test コマンドを通しましょう。yarn testで、自動的にテストができるようになります。
今いまは、実行するとエラーが出ると思います。

最後に、package.jsonのBrowserlist{}の次に

"jest": {
  "moduleFileExtensions": [ "js", "vue" ],
  "transform": {
    ".*\\.(vue)$": "vue-jest",
    ".*\\.(js)$": "babel-jest"
  }
}

と追加してください。
vue拡張子のものは、vue-jest に、js拡張子のものは babel-jest に transform させるようにしています。以上で、package.jsonの変更はおしまいになります。

3:Red → Green の順番で実装を進める
やっと本題です。
src/components/に、SignUp.vue, SignUp.spec.jsを作成します。
SignUp.vue

<template><h1>ログイン</h1></template>

SignUp.spec.js

const SignUp = require('./SignUp.vue');
const vtl = require('@testing-library/vue');
const { render, screen } = vtl;

it('ログインヘッダー', () => {
  render(SignUp);
  const header = screen.queryByRole('heading', { name: 'ログイン' });
  expect(header).toBeNull();
})


このような状態で、yarn test を実行するとエラーが出ると思います。


おめでとうございます!TDD開発の最初のREDに行き着きました。これは正しいエラーです。
エラー内容:expect(header).toBeNull(); が Null では無いため。
SignUp.vueでは、<h1>にログインという文字が入っているため、Null ではありません。なので、

×:expect(header).toBeNull();
○:expect(header).not.toBeNull();

と書き換えることで、テストが通るようになります。


次に、クリックイベントのテスト等を設定するための下記ライブラリを追加します。

npm i -D @testing-library/jest-dom @testing-library/user-event

SignUp.spec.jsを下記のように変更します。

import SignUp from './SignUp.vue';
import { render, screen } from '@testing-library/vue';
import "@testing-library/jest-dom";

describe("ログイン", () => {
  describe('レイアウト', () => {
    it('ログインヘッダー', () => {
      render(SignUp);
      const header = screen.queryByRole('heading', { name: 'ログイン' });
      expect(header).toBeInTheDocument();
    })
  })
})


こちらもpassしますね。もし、SignUp.vueを下記のように「!」を加えたらどうなるでしょうか。

<template> <h1>ログイン!</h1></template>
スペック失敗


このように、jestがエラーを吐きます。これの積み重ねで、堅牢なアプリケーションを構築できるようになります。「!」は戻しましょう。

さて、spec自体にもいくつかの機能を紹介します。describe と it です。
SignUp.spec.jsを下記のように変更します。

import SignUp from './SignUp.vue';
import { render, screen } from '@testing-library/vue';
import "@testing-library/jest-dom";

describe("ログイン", () => {
  describe('レイアウト', () => {
    it('ログインヘッダー', () => {
      render(SignUp);
      const header = screen.queryByRole('heading', { name: 'ログイン' });
      expect(header).toBeInTheDocument();
    })
  })
})
スペック成功


こうすることで、よりspecが何をやっているか、が可視化されたと思います。
あとは

describe("ログイン", () => {
  describe('レイアウト', () => {
    it('ログインヘッダー', () => {
      render(SignUp);
      const header = screen.queryByRole('heading', { name: 'ログイン' });
      expect(header).toBeInTheDocument();
    })
    it('ログインボタン', () => {
      render(SignUp);
      const button = screen.queryByRole("button", { name: 'ログインボタン' });
      expect(button).toBeInTheDocument();
    })
  })
})

という風に書いていくことで

スペック失敗

このように、テスト項目を増やしてRED→GREENとしていきます!

<template>
  <h1>ログイン</h1>
  <button>ログインボタン</button>
</template>

まとめ
今回は、TDD の RED → GREEN までを説明していきました。次回は、Refactoring部分を説明していきたいと思います。