vue.js
jest

JestでプレーンJS/Vue.jsのTDDを行う

(この記事は「JavaScript2 Advent Calendar 2018」の6日目です)

fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます:bow:

JavaScriptのテスティングフレームワーク「Jest」を使って、プレーンJSとVue.jsのテストを行ってみます

TDD(Test Driven Development)の流れで実施していきます

npmのインストール

npmを利用可能にするため、下記から適宜Node.jsをインストールしてください
https://nodejs.org/ja/download/

package.jsonを作成

適当なフォルダを掘って、その配下に、下記ファイルを作成します

package.json
{
    "scripts": 
    {
        "test": "jest"
    }
}

Jestのインストール

package.jsonを作ったフォルダ配下に移動し、シェル/コマンドプロンプトで、以下コマンドを実行します

シェル/コマンドプロンプト
npm install --save-dev jest

(なお、npmの代わりにyarnを使ってもOKですが、ここでは解説は割愛するので、分かる方だけ自己解決してください)

テストを作成

TDDをするので、まずテストを作成します

expect()に実測値(actual)を入れ、それをtoBe()で期待値(expected)と比較することで、「assertEquals」のような動きになります

Jestでのテストファイルは、ファイル名に「test」もしくは「spec」を入れると、テスト実行対象になります

テスト対象メソッドとして、add()という、足し算を行うメソッドを想定します(ただし、TDDするので、この時点ではロジック自体を作りません)

sample.test.js
test( '1 + 2 = 3', () => 
{
    expect( add( 1, 2 ) ).toBe( 3 )
} )

テストを実行(Redパターン)

以下コマンドでテストを実行します

シェル/コマンドプロンプト
npm test

ロジックが無いので、当然、まっ赤っかのテスト失敗(Red)になります
image.png

ロジックを追加し、テストを実行(Greenパターン)

ロジックを実装します(現在のテストを通す、最も小さい実装を行います)

なお、module.exportsで指定するのは、メソッド名です

sample.js
function add( a, b )
{
    return 3;
}
module.exports = add;

TDDに慣れていない方だと、「いやいや、そんなロジック作らないでしょ」と言いそうですが、より複雑なケースや、ロジックの組み合わせをする中で、「現在、詳細に実装しなくても、取り敢えずの値を返してくれればOK」というシチュエーションは、案外あります

そういう場合、上記のような、ダミー実装をして、その他の実装を進めるパターンも往々にしてあります(流石にここまで簡単だと「実装しようよ」となりますけど、あくまで実装し切らない状況もある、というTDDの例です)

テスト側も、実装を呼べるよう、requireを追加します

sample.test.js
const add = require( './sample' )

test( 'add', () => 
{
    expect( add( 1, 2 ) ).toBe( 3 )
} )

改めて、テストを実行します

シェル/コマンドプロンプト
npm test

テストが成功し、緑(Green)になりました
image.png

テスト追加でRedになり、ロジック改修(Refactoringパターン)

テストを追加します

sample.test.js
const add = require( './sample' )

test( 'add', () => 
{
    expect( add( 1, 2 ) ).toBe( 3 )
    expect( add( 2, 3 ) ).toBe( 5 )
} )

改めて、テストを実行します

シェル/コマンドプロンプト
npm test

追加したテスト部分がテスト失敗となります
image.png

ロジックをリファクタリングします

sample.js
function add( a, b )
{
    return a + b;
}
module.exports = add;

改めて、テストを実行します

シェル/コマンドプロンプト
npm test

テスト成功しました
image.png

なお、2ケースのテストを実施していますが、実行結果には、「1 passed」と出ているので、これを2ケースにしたい場合は、以下のようにテストメソッドを別々にします

sample.test.js
const add = require( './sample' )

test( 'add 3', () => 
{
    expect( add( 1, 2 ) ).toBe( 3 )
} )

test( 'add 5', () => 
{
    expect( add( 2, 3 ) ).toBe( 5 )
} )

こうすると、「Tests」が「2 passed」になります
image.png

参考:テストを通す最低限の実装とは?

ちなみに、「テストを通す最低限の実装」という観点だと、実は、こんな実装もあり得ます

sample.js
function add( a, b )
{
    return a == 1 ? 3 : 5;
}
module.exports = add;

「なんだこりゃ?」という実装に見えますが、こういう発想を通して、「最低限のロジック実装」とは何なのか、を考える思考プロセスは、とても大事です

この感覚が分からないと、ムダに汎用性が高い、とか、やたらロバストなロジックを、「本当に必要となる前に」書いてしまい、改修やエンハンスがやりにくくなったり、延々と使いもしないロジックをメンテナンスする…といった罠にハマります

こうした、「技術的負債」を作らないための考え方が、TDD/アジャイル開発の始祖である「eXtreme Programming」では、「YAGNI(You ain't gonna need it)」という原則として挙げられています

Vue.jsのメソッドをテストする(Redパターン)

ここまでは、プレーンJSのテストだったので、今度は、Vue.jsのメソッドをテストします

まず、JestでVue.jsを使えるようにします

シェル/コマンドプロンプト
npm add vue jest

同フォルダ配下にテストを追加します

vue_sample.test.js
const app = require( './vue_sample' )

test( 'vue_add 3', () => 
{
    expect( app.vue_add( 1, 2 ) ).toBe( 3 )
} )

test( 'vue_add 5', () => 
{
    expect( app.vue_add( 2, 3 ) ).toBe( 5 )
} )

中身が空のvue_sample.jsも作っておきます

vue_sample.js

テストを実行すると、プレーンJS用とVue.js用の両方のテストが走ります

シェル/コマンドプロンプト
npm test

プレーンJS用テストは成功し、Vue.js用テストはロジック未実装のため失敗します
image.png

Vue.jsのメソッドを追加し、テストする(Greenパターン)

普通のWebページに適用するのと同じようなVue.jsでロジックを実装します(リファクタリングはしないパターンで行きます)

vue_sample.js
const Vue = require( 'vue' )

var app = new Vue
( {
    el: '#app',
    data: 
    {
    }, 
    methods: 
    {
        vue_add: function( a, b )
        {
            return a + b
        }, 
    }, 
} )
module.exports = app

テストを実行します

シェル/コマンドプロンプト
npm test

テストは成功しますが、「#app」は未定義のため、warningが出ています
image.png

気になるときは、el部をコメントアウト(もしくはコンポーネント化)してください

vue_sample.js

var app = new Vue
( {
//  el: '#app',
    data: 

image.png

補足:axiosでのAPI呼出をテストする際の注意点

Vue.jsで、API呼出に利用する「axios」のテストを書く際は、前段でJestにaxiosを導入します

シェル/コマンドプロンプト
npm add axios jest

また、非同期呼出のままだと、テスト実施時のexpectで空振りとなるため、async/awaitの付け忘れに注意してください

axios_sample.test.js
const axios = require( 'axios' )

test( 'axios_get', async () => 
{
    var results = []
    await axios.get( 'https://qiita.com/api/v2/items?query=elixir' )
    .then( response => { results = response.data } )

    expect( results.length ).toBe( 20 )
} )

終わり

こんな感じで、Jestを使うと、気軽にプレーンJSも、Vue.jsも、TDDしていけます

なお、Jest自体は、特にJSフレームワークを選ぶものでは無い、JavaScriptであれば使えるテスティングフレームワークですので、ReactやAngularをお使いの方でもトライしてみてください

p.s.「いいね」よろしくお願いします

ページ左上の image.pngimage.png のクリックを、どうぞよろしくお願いします:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!:tada: