jsdom
React

JSDOMとReact.addons.TestUtilsでReactをヘッドレスにテストする

More than 3 years have passed since last update.

JSDOMはnode環境でDOMをシミュレートするやつ。React.addons.TestUtilsはReactに同梱されているテストツール。この2つがあわさり最強に見える。

今作ってるライブラリのために、しばらくブラウザ使わずにテストしてたけど、ブラウザ立ち上げる必要なくて非常に快適。これ https://github.com/mizchi/arda/tree/master/test

JSDOM、昔はとにかく不安定な印象だったけど、最近はよくできてる印象。見なおした。

コード

# globalにdocumentとwindowを定義することで、DOM環境を参照できるようにする
# 必ずReactの前に読み込むこと
jsdom = require('jsdom').jsdom
global.document  = jsdom('<html><body></body></html>')
# global.window    = document.parentWindow # 追記 jsdom v4.0で parentWindow -> defaultView
global.window    = document.defaultView
global.navigator = window.navigator

# TestUtilsを使うためにreact/addonsを読み込む
React = require 'react/addons'

# 0.13.0-beta.1 以降で使える ES6 Class記法
# ※ 別にReact.createClassでも良いがgetInitialStateになる
class HelloComponent extends React.Component
  constructor: ->
    super
    @state = cnt: 0

  render: ->
    React.createElement 'button',
      className: 'counter'
      ref: 'counter'
      onClick: @onClick.bind(@)
    , 'cnt:'+@state.cnt

  onClick: ->
    @setState cnt: @state.cnt+1

# アサーション
assert = require 'assert'

# renderIntoDocumentはDOMにマウントせずcomponentを生成する
# ただしDOM環境は必要
component = React.addons.TestUtils.renderIntoDocument React.createFactory(HelloComponent)()

assert.equal component.refs.counter.getDOMNode().innerHTML, 'cnt:0'

# クリックをシミュレート
React.addons.TestUtils.Simulate.click component.refs.counter.getDOMNode()

# 再レンダーされているか確認
assert.equal component.refs.counter.getDOMNode().innerHTML, 'cnt:1'

# もう二回ぐらい叩いてみる
React.addons.TestUtils.Simulate.click component.refs.counter.getDOMNode()
React.addons.TestUtils.Simulate.click component.refs.counter.getDOMNode()
# 確認
assert.equal component.refs.counter.getDOMNode().innerHTML, 'cnt:3'

実はちょっと前まで動かなったけど、最近動くようになった。

ReactTestUtils.Simulate.click does not work with jsdom · Issue #1185 · facebook/react https://github.com/facebook/react/issues/1185

懸念点

jsdom v3.3.1 がまだ io.js v1.1.0でビルドできないので node v0.10を使った。jsdomの初期化、ちょっとだけ時間かかって、テストの実行のたびに0.6秒ぐらい必要。まあPhantomよりは遥かにマシ。

参考