16
19

More than 5 years have passed since last update.

2016年から始めるフロントサイド~テスト編~

Last updated at Posted at 2016-03-11

テスト

  1. mocha
  2. expect
  3. jest
  4. karma
  5. enzyme

テスト用のライブラリは色々ありますが、React関係のプロジェクトでよく使われているものだけ取り上げます。

mocha

インストール
$ npm install --save-dev mocha

# es6でテストを書く場合はインストール
$ npm install --save-dev babel-register

テストコードを書きます。

test/calc.spec.js
import expect from 'expect'

describe('add', () => {

  let add
  beforeEach(() => {
    add = (x, y) => {
      return x + y
    }
  })

  afterEach(() => {
    add = () => {}
  })

  it('add(1, 2)', (done) => {
    expect(add(1, 2)).toEqual(3)
    done()
  })
})

あとはpackage.jsonのscriptでtestを追加して動かします。

package.json
{
  ...,

  "scripts": {
    "clean": "rimraf lib dist",
    "compile": "babel --presets es2015 src/ -d lib/",
    "build": "cross-env NODE_ENV=production webpack",
    "build:dev": "npm run compile && npm run build",
    "lint": "eslint src",
    // mochaはデフォルトではtest/を探索するので、別のディレクトリを探索してほしいときは
    // --recursiveのあとにそのディレクトリを指定します
    // --compilersで指定のフォーマットにコンパイルしてくれます
    "test": "mocha --compilers js:babel-register --recursive"
  },

  ...,
  }
テスト
$ npm run test

expect

インストール
$ npm install --save-dev expect

toBe, toEqualなどは直感的に使えそうなので、Spyを少し詳しく見てみる。

Spy

createSpy
export function createSpy(fn, restore = noop) {
  if (fn == null)
    fn = noop

  assert(
    isFunction(fn),
    'createSpy needs a function'
  )

  let targetFn, thrownValue, returnValue

  const spy = function spy() {
    spy.calls.push({
      context: this,
      arguments: Array.prototype.slice.call(arguments, 0)
    })

    if (targetFn)
      return targetFn.apply(this, arguments)

    if (thrownValue)
      throw thrownValue

    return returnValue
  }

  spy.calls = []

  spy.andCall = (otherFn) => {
    targetFn = otherFn
    return spy
  }

  spy.andCallThrough = () =>
    spy.andCall(fn)

  spy.andThrow = (object) => {
    thrownValue = object
    return spy
  }

  spy.andReturn = (value) => {
    returnValue = value
    return spy
  }

  spy.getLastCall = () =>
    spy.calls[spy.calls.length - 1]

  spy.reset = () => {
    spy.calls = []
  }

  spy.restore = spy.destroy = restore

  spy.__isSpy = true

  spies.push(spy)

  return spy
}

createSpyでspyのconstructorを返す。引数にfnを指定するとaddCallThrough()でfnの返り値を取れる。addCall(fn)はspy作成後に好きなfunctionを設定できる。

spyOn
export function spyOn(object, methodName) {
  const original = object[methodName]

  if (!isSpy(original)) {
    assert(
      isFunction(original),
      'Cannot spyOn the %s property; it is not a function',
      methodName
    )

    object[methodName] = createSpy(original, () => {
      object[methodName] = original
    })
  }

  return object[methodName]
}

object[methodName]をspyにする。addCallThrough()で元のfucntionが実行出来る。addCall(fn)で好きなfunctionに入れ替えられる。

一度試してみる。

test/calc.spec.js
import expect, { createSpy, spyOn } from 'expect'

describe('add', () => {

  const calc = {
    add: () => {},
    multiply: () => {}
  }

  let add
  beforeEach(() => {
    add = (x, y) => {
      return x + y
    }
    calc.add = add
  })

  afterEach(() => {
    add = () => {}
    calc.add = add
  })

  it('add(1, 2)', (done) => {
    expect(calc.add(1, 2)).toEqual(3)
    done()
  })

  it('add spy', (done) => {
    // calc.addとspyは同じ参照を指す
    // calc.add(1, 2)はspy(1, 2)なのでspy.callsに結果がpushされる
    // その結果、spyがspy.callsから結果をのぞける
    let spy = spyOn(calc, 'add')
    expect(spy.andReturn(3).call(this)).toEqual(3)
    // 元のcalc.addを使う
    expect(spy.andCallThrough().call(this, 1, 2)).toEqual(3)
    // functionの入れ替え
    expect(spy.andCall((x, y) => {
      return x *2 + y * 2
    }).call(this, 1, 2)).toEqual(6)
    done()
  })
})

想定通りの結果となった。

jest

  • 公式サイト
  • facebook製のUnit testライブラリ。assertionにはjasmineというのを使用しているようだ。
  • これ一つで何でもできるらしい。mocha+expect+karma相当?
インストール
$ npm install --save-dev jest-cli

# babelを使用する場合合わせてインストール
$ npm install --save-dev babel-jest babel-polyfill

npm runで実行できるようにする。
babelを使う場合はjestのpropertyを作成する。

package.json
{
  ...,

  "scripts": {
    ...,

    "test": "mocha --compilers js:babel-register --recursive",
    "jtest": "jest"
  },
  // babelを使う場合に追加
  "jest": {
    "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
    "testFileExtensions": ["es6", "js"],
    "moduleFileExtensions": ["js", "json", "es6"]
  },

  ...,
}

__tests__ディレクトリを作成してテストを書く。

__tests__/calc-test.js
// 特にexpectをimportする必要はない

describe('add', () => {

  const calc = {
    add: () => {},
    multiply: () => {}
  }

  let add
  beforeEach(() => {
    add = (x, y) => {
      return x + y
    }
    calc.add = add
  })

  afterEach(() => {
    add = () => {}
    calc.add = add
  })

  it('add(1, 2)', (done) => {
    expect(calc.add(1, 2)).toEqual(3)
    done()
  })
  /* expectしか使えない?
  it('add spy', (done) => {
    let spy = expect.spyOn(calc, 'add')
    expect(spy.andReturn(3).call(this)).toEqual(3)
    expect(spy.andCallThrough().call(this, 1, 2)).toEqual(3)
    expect(spy.andCall((x, y) => {
      return x *2 + y * 2
    }).call(this, 1, 2)).toEqual(6)
    done()
  })
  */
})

どうやらexpect()しか使えないようだ。。別のライブラリを使ってる可能性ありそう・・

テスト
$ npm run jtest

karma

インストール
# mochaとwebpackを使用することを想定
$ npm install --save-dev karma karma-chrome-launcher karma-mocha karma-webpack

configファイルを初期化します。

karma
$ ./node_modules/karma/bin/karma init karma.conf.js

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> mocha

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> test/**/*spec.js
>

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

webpackでes6をコンパイルするので、configファイルを修正します。

karma.conf.js
module.exports = function(config) {
  config.set({

    ...,

    // webpack指定
    preprocessors: {
      'test/**/*.js': ['webpack']
    },

    // es6をコンパイルするように設定
    webpack: {
      module: {
        loaders: [
          {
            test: /\.js$/,
            loaders: ['babel-loader?presets[]=react&presets[]=es2015&cacheDirectory'],
            exclude: /node_modules/
          }
        ]
      },
    },

    ...,

  })
}

npm runで実行できるようにする。

package.json
{
  ...,

  "scripts": {
    ...,

    "test": "mocha --compilers js:babel-register --recursive",
    "jtest": "jest",
    "test-browser": "karma start"
  },

  ...,
}
テスト
$ npm run test-browser

enzyme

インストール
$ npm install --save-dev enzyme

# 依存するライブラリのインストール
$ npm install --save-dev react-addons-test-utils react-dom

軽く試してみる。

About.js
import React from 'react'

export default React.createClass({
  render() {
    return <div>About</div>
  }
})
About.spec.js
import React from 'react'
import expect from 'expect'
import { shallow } from 'enzyme'
import About from '../src/react-router/modules/About'

describe('<About />', () => {

  it('should render there <About />', () => {
    const about = shallow(<About />)
    expect(about.find('div').length).toEqual(1)
  })
})

これでmochaを実行する。
アプリケーションがReact前提なら便利かも。

テストは色々なライブラリやフレームワークがあってどれを使うか悩みますね!

16
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
19