初めてjest(というかテストコード)を触る人に向けた、導入と考え方を、初心者なりにまとめます。
コツを抑えられればサクサクテスト記述できるのですが、それを掴むまで結構苦労したので、その知見を紹介できれば、と思います。
対象のソースコードはNode.jsとVue.jsです。
はしがき
最近、結婚披露宴を行い、そこでスライドショーアプリを自作しました。
詳細はこちら。
このアプリはNode.jsとVue.jsで作っており、jestでテストコードを書いています。
本記事では、その時の知見をまとめました。
JavaScriptテストコードの基本
テストコードの構造は基本的には以下のように書きます
describe('sample test', () => {
test('test1', () => {
// テスト処理
})
test('test2', () => {
// テスト処理
})
})
テスト時には、test
関数内の処理が実行され、結果のチェックを行います。describe
関数は複数のtest
をグルーピングします。
サンプルソース例を結構見てみましたが、test
関数を直に書いているサンプルはなかったので、describe
関数から定義するのがお作法なようです。
Jestの他にもJavaScriptのユニットテストフレームワークは存在します。mocha+chai
、karma+jasmine
、などが有名なようです。
ただ、今回テストを行うソースがNode.js環境だったので、親和性の高いJestを採用しています。
Jest de テストコード - mock編
ユニットテストを行うにあたって、真っ先に障壁になるもの。
それは、require
やimport
している他ファイルの処理です。
例えば、fs
をrequire
しているソースをテストする場合を考えます。操作するファイルが本番環境以外で取得できない(API経由で取得するなど)場合、テスト時に実際のfs
の関数が実行されてしまうのはよろしくないです(ファイルがあったとしても関数を実行するべきではないですが)。
そこで、require
されるfs
をモック化します。
mockファイルの生成
npmパッケージをモック化する場合、node_module
と同じディレクトリに__mock__
という名前でディレクトリを作り、その中にrequire
される名前と同じ名前のjsファイルを置きます。
__mock__
ディレクトリが存在していた場合、テストコード実行時にjestはまず__mock__
ディレクトリを探し、そこに定義がなければnpmパッケージの処理を実行します。ですので、__mock__
内に定義しておけばモック化することができます。
your-project
├── node_modules
│ └── fs
└── __mock__
└── fs
この__mock__
配下のfsにモックコードを書きます。
下記はfs.mkdirSync関数のモック化例です。
// モック元ファイルの指定
const fs = jest.genMockFromModule('fs')
// ディレクトリ作成関数mkdirSyncのモック記述
function mkdirSync(directoryPath) {
var file = { directoryPath: 'dummy' }
}
// モック関数の登録
fs.mkdirSync = mkdirSync
module.exports = fs;
こうすることで、テスト対象コード内でfs.mkdirSyncの処理があった場合、モック化した関数のみを実行してくれます。
一部関数のみのモック化
ただ、わざわざmockファイルを作る必要はないケースというのもあります。
例えば、テスト対象コード内で一度だけしか呼ばれない別ファイルの関数の処理をmock化したいとか、一つのテストケース内でしか呼ばれないとか言った場合です。
以下はmyApp.js
というファイルのテストコード内で、myModule.js
のfuncA
という関数をsample test
というテストケースの中でのみモック化する場合のサンプルです。
const myApp = require('../myApp')
import myModuleMock from 'DIRECTORY_PATH/myModule'
describe('all test', () => {
test('sample test', () => {
// mock関数の定義
const spy = jest.spyOn(myModuleMock, 'funcA').mockImplementation(() => {
return true
})
// テスト実行
myApp.exec()
// mock関数がテスト内でコールされたか
expect(spy).toHaveBeenCalled()
})
})
注意しなければいけないのが、spyOn()
で登録した関数は、あくまで関数が呼ばれたかどうかの状態を監視する役目しか持たないということです。
つまり、ただspyOn()
しただけだと、実際のmyModule.funcA
もコールされてしまいます。
これをモック化するためには、mockImplementation
の記述を追加し、モック処理を書く必要があります。
これで関数単位のモック化ができます。
Jest de テストコード - Vue.js編
正直難しいものはないです。
まずはVueのテスト用パッケージインストールします。
npm install --save-dev @vue/test-utils
あとはテストコードにインポートして、以下の通り書けばVueコンポーネントのテストが実施できます。
以下は、Start
という名前のコンポーネントのテストコードです。
// 子コンポーネントがある場合、shallowMountで定義
import {shallowMount} from '@vue/test-utils'
import Start from '@/components/Start'
wrapper = shallowMount(Start, {
mocks: { $router: router }
})
test('sample test', () => {
// Vueインスタンスかどうか確認する
expect(wrapper.isVueInstance()).toBeTruthy()
// templateのDOM要素をclassで指定し、アクションを行う
wrapper.find('.opening_content').trigger('click')
// 関数実行する場合、.vm.funcでコール
wrapper.vm.getImageJson()
// data要素を取得
wrapper.vm.$data.showNum
// emitイベント呼び出し
wrapper.emitted('startSlideShow')
})
他コンポーネントとのやり取りについては、インスタンス宣言時、モック化させることができます。