やりたいこと
node.js環境でsinonのfakeServerを使いたい。
なんで?
sinonのfakeServerを使うと、特定のurlへのアクセスに対して任意のステータスコード、ヘッダー、レスポンスボディを返すstubサーバを作る事ができ、APIを呼んだらこうなる、というテストを書く事ができる。
しかし、このfakeServerはnode環境だと使えない。sinon.jsの初期化処理を眺めるとわかるが、global環境にXMLHttpRequestが定義されているか否か、そのXMLHttpRequestオブジェクトにwithCredentials
属性があるか否か、といったことをチェックしており、実行環境にXMLHttpRequestがあることが前提となっている。
対処法
てことは、globalにXMLHttpRequestのインタフェースを実装してあげれば良さそうである。自分で一通りインタフェースを実装すれば良いのかもしれないが、その前にGoogle先生に聞いてみると、node-xmlhttprequestなる、XMLHttpRequestをnode.js上で実装したモジュールが出てくる。一通りXMLHttpRequestのインタフェースが実装されているので、テスト環境においてこのモジュールをglobal.XMLHttpRequest
に設定してあげると、sinon.jsの初期化時にXMLHttpRequestが検出され、fakeServer
がちゃんと動作するようになる。
コード
以下サンプルコード。XMLHttpRequestをglobalに設定するファイル(browser.js)とsinon
の読み込みは別ファイルにして、必ずbrowser.js
を先に読み込ませる。一つのファイル内でどうにかできないもんかと思いつつ、ES6のimportやglobalが最終的にどうコンパイルされるかまで見てないのでよくわからず。。。
個人的な事情で、mocha
とchai
のテストコードです。
mocha --compilers js:babel/register test.js
// ブラウザ環境っぽいものを作る
import jsdom from 'jsdom';
import jQuery from 'jquery';
import { XMLHttpRequest } from 'w3c-xmlhttprequest';
global.XMLHttpRequest = XMLHttpRequest;
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = document.parentWindow;
global.$ = jQuery(window);
// chai使ってます
import './browser';
import chai, { expect } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
chai.use(sinonChai);
describe('FakeServer in node.js', () => {
let server;
let spy;
beforeEach(() => {
server = sinon.fakeServer.create();
spy = sinon.spy();
});
afterEach(() => {
server.restore();
});
it('fake responseが返ること', () => {
server.respondWith('GET', '/sample.json', [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({message: 'Hello World'})
]);
$.getJSON('/sample.json')
.then((data) => {
spy(data);
});
server.respond();
expect(spy).to.have.been.calledWith({message: 'Hello World'})
});
});
まとめ
node環境でもfakeServer使えそうです。
同じようなXMLHttpRequest in node.js実装として、xhr2というのもあったのだけど、こっちではうまく動きませんでした。withCredentials
が実装されてないのが原因っぽいですが、詳細は追ってません。