LoginSignup
3
0

More than 5 years have passed since last update.

browser-envを使ってブラウザに固有のグローバル変数をNode.jsに用意する

Posted at

要旨

  • ブラウザに固有のグローバル変数(window, documentなど)1 を使うコードをNode.jsで走らせるために browser-env を使う
  • ava を使ったテストに browser-env を使用したサンプルを紹介する

背景・課題

ブラウザで動かすことを前提に書いたJavaScriptをNode.jsで走らせたいことがある。例えばユニットテストがそうだ。

次のようなサンプルを用意した。

このサンプルはlocationのhashを表示する、という操作を実行している(テストのために遠回りに書いている)。ビルドされたファイルをブラウザで開くと期待通り動作する確認できる。

しかし次のような関数を AVA を使ってユニットテストを実行した時に問題が発生した。

src/url.js
const getUrlHash = href => {
  return new URL(href).hash;
};

export { getUrlHash as default };

テストコードは次の通りだ。

test/url.test.js
import test from 'ava';
import getUrlHash from '../src/url';

test('getUrlHash should return the hash of the URL', t => {
  const actual = getUrlHash('http://example.com/path/to?key=value#xyz');
  const expected = '#xyz';
  t.is(expected, actual);
});

残念ながらブラウザで問題なく動いたこのコードも、ava実行時は URL が定義されていないとしてエラーになってしまう。

ava
$ ava

1 failed

url  getUrlHash should return the hash of the input URL

/path/to/try-browser-env/src/url.js:7

Error thrown in test:

ReferenceError {
  message: 'URL is not defined',
}

getUrlHash (src/url.js:7:3)
Test.fn (test/url.test.js:5:18)

原因はNode.js環境とブラウザ環境の違いである。Node.jsには URL という関数はグローバルに定義されていないが、(一部の)ブラウザにおいては URL は定義されている

node
// node cliで確認する
$ node
> URL
ReferenceError: URL is not defined
...
chrome-developer-tool/console
// ChromのDeveloper Toolのコンソールで確認する
> URL
> ƒ URL() { [native code] }

解決方法

AVAによると browser-env を使うことで解決できる。使い方は AVAのレシピ にあるがポイントだけ掻い摘んでおこう。

インストール

$ npm install --save-dev browser-env
// or
$ yarn add --dev browser-env

セットアップ

test/helpers/ ディレクトリに以下のようなファイルを用意する。

test/helpers/setup-browser-env.js
import browserEnv from 'browser-env';

browserEnv();

package.jsonの ava.require にこのヘルパーファイルを設定する。

package.json
{
  "ava": {
    "require": [
      "./test/helpers/setup-browser-env.js"
    ]
  }
}

これでAVAのテスト実行時にブラウザ環境を模倣することができたので URL の ReferenceError になることはない。もちろん src/ ディレクトリのファイルは変更しておらず、ブラウザでも問題なく動く。

※補足

上記のように browserEnv への引数を空にすると全てのブラウザ固有のグローバル変数がNode.jsのグローバルスコープに追加されてしまう。ブラウザ環境を模倣するには良いが、把握していない/必要としないグローバル変数を作るのは健全ではない。使用するものだけに限定すると良いだろう。

test/helpers/setup-browser-env.js
import browserEnv from 'browser-env';

browserEnv(['URL']);

また個別のテストファイルに対してブラウザ固有のグローバル変数を導入したいときは、各テストファイルに記載しても問題ない。

test/url.test.js
import test from 'ava';
import getUrlHash from '../src/url';
import browserEnv from 'browser-env';
browserEnv(['URL']);

test('getUrlHash should return the hash of the input URL', t => {
  const actual = getUrlHash('http://example.com/path/to?key=value#xyz');
  const expected = '#xyz';
  t.is(expected, actual);
});

その他のブラウザ変数

browser-envが用意するのは URL だけではない。 documentlocation などのブラウザ固有のグローバル変数もモックしてくれる。

node
import browserEnv = require('browser-env');
browserEnv(['location'], {url: "http://example.com/path/to?key=value#xyz"});

location.pathname
//'/path/to'
location.hash
// '#xyz'

browser-envは jsdom を使っているので、browserEnv の第2引数の詳細は JSDOM を参考にするとよい。

参考


  1. AVA"browser specific globals" と呼んでいるもの 

3
0
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
3
0