jsdomを使ってMutationObserverをテストする方法

github.com

JavaScriptでブラウザのAPIを使ったコードをテストするときはjsdomを使うと思う。大抵のDOMのAPIは揃ってるのでテストできるけどMutationObserverをテストしようとしたところjsdomはMutationObserverを持ってないのでテストできなくて困った。「MutationObserverなんてないよ」と怒られてしまう。

Polyfillを使って回避する

ようはjsdomにMutationObserverがないからMutationObserverのそれに近いものを足せばOKということになる。

github.com

ES3ブラウザに向けて書かれたshimを作っている方がいるのでこれをありがたく使わせて頂く。

$ npm install -D mutationobserver-shim

入れたらテストをこう書く。自分はAVAを使っているのでAVAの例。

import test from 'ava';
import delay from 'delay';
import jsdom from 'jsdom';
import m from '.';

const dom = new jsdom.JSDOM();
global.window = dom.window;
global.document = dom.window.document;

require('mutationobserver-shim');

global.MutationObserver = window.MutationObserver;

test('MutationObserver test', async t => {
    delay(500).then(() => {
        const el = document.createElement('div');
        el.id = 'late';
        document.body.appendChild(el);
    });

    const checkEl = await m('#late');
    t.is(checkEl.id, 'late');
});

'm'はテストしたい自分のモジュール。mは内部でMutationObserverを利用している。

MutationObserverはブラウザではwindowオブジェクトにぶら下がっているけれどNode.jsにはそもそもwindowオブジェクトはない。ので、まずNode.jsのglobalにjsdomのwindowオブジェクトを作ってあげる。

require('mutationobserver-shim');

windowオブジェクトできたうえで、副作用requireしてMutationObserverを定義してあげる。するとこれでテストできるようになる!

めでたい

とりあえずこれでMutationObserverのテストが出来ていると思う。実際にテスト書いて通っている。

今件5年も前からIssueあるけど進捗がない。1度jsdomにPR出されてたけどパフォーマンスと実装の保守が難しいとかなんとかでリジェクトされてそのまま月日が経っている。コメントの中にAureliaのプロジェクトでTypeScriptでpolyfill書いて同じようにやっている人がいるけど、これをJS環境にもってきてもうまく動かなかった(これはたぶん自分の力量がないせいだと思う)

あくまでこの書いた方法はpolyfillを使っているので仕様策定され実際のブラウザで実装されているものとは少し挙動が違う。ので完全にテストできているとは言えないかもしれないけど、現状これしかMutationObserverのテストはできないと思う。

本来はjsdom本体に実装されて欲しいけど現状はそういうこと、ということで。