この記事は何か
Windowsのスクリプティング環境としては未だ根強い人気のWSH(Windows Scripting Hosts)をJScriptで書くときにもユニットテストしたいので、QUnitを使って何とかする話です。
TL;DR;
- 責務分割、モジュール化を行う
- 処理本体は適宜必要なjsファイルに分けて定義
- WSH起動ファイルをwsfファイルにしてjsファイルをインポートして利用する
- QUnitでは各種jsファイルを読み込んでテストする
- ActiveXObjectを利用するため、ブラウザーにはIEを利用する
- [インターネットオプション]-[セキュリティ]-[ローカル イントラネット]のレベルをカスタマイズする
- [ActiveX コントロールとプラグイン]-[スクリプトを実行しても安全だとマークされていない ActiveX コントロールの初期化とスクリプトの実行]を[ダイアログを表示する]に変更
- [インターネットオプション]-[セキュリティ]-[ローカル イントラネット]のレベルをカスタマイズする
- ActiveXObjectを利用するため、ブラウザーにはIEを利用する
- WScript.Echoは使えないので、デバッグ出力用関数オブジェクトを外部から注入する
- QUnitからはconsole.logを実行
- wsfからはWScript.Echoを実行
問題点
普通にべた書きしたJScriptなWSHをユニットテストしようとしても次のような問題点があります。
- そもそもテスティングフレームワークがあるのか
- ブラウザからはActiveXObjectを使ってPCリソースにアクセスできない
- ファイルの先頭から処理が書いてあるからテストできない
解決法
これらの問題点を、本記事では次のように解決します。
1. そもそもテスティングフレームワークがあるのか
WSH用のテスティングフレームワークとしては、実はScriptUnitというものがあります。しかし、JUnit風の専用のテストランナーで実行する形であり、デバッグ実行などもできないため、取り回しがあまり楽ではありません。さらに、何か問題があっても絶対的に情報量が少ないです。
そもそもJScriptはJavaScriptの一派です。JavaScriptならすでに多くのテスティングフレームワークがあります。これを利用しない手はありません。
そこで、本記事ではブラウザから利用するJavaScript用テスティングフレームワークの古株であるQUnitを使います。もちろん、本記事の「キモ」はQUnitを使うことではないので、たぶんJasmineなどでも同様に対応できるはずです。
2. ブラウザからはActiveXObjectを使ってPCリソースにアクセスできない
しかし、ブラウザからJScriptを実行しようとすると一つ問題があります。それはActiveXObjectを用いたファイル操作など、PCリソースへのアクセスに制限がかかることです。
この問題を解決するため、QUnitのテストページはみんな大好き**IE(Internet Explorer)**で表示しましょう(Microsoft EdgeはActiveX使えないのでダメ)。
なお、VS付属のIISExpressなど開発サーバーを使って表示する際は、ActiveXObjectが無効になってしまうので、以下の手順でローカルイントラネットゾーンについてIEのセキュリティレベルを少し落とす必要があります。
設定方法
-
IEを起動して[インターネット オプション]を表示する
-
[ActiveX コントロールとプラグイン]-[スクリプトを実行しても安全だとマークされていない ActiveX コントロールの初期化とスクリプトの実行]を[ダイアログを表示する]に変更する(予期せぬスクリプト実行を防ぐため、[有効にする]にはしないこと)
![ActiveXObjectを使ったスクリプトを実行可能にする]
(https://qiita-image-store.s3.amazonaws.com/0/12039/5a97715e-ce5a-8452-8f23-4ad6a15a38a9.jpeg)
これで、JScript中にある次のようなActiveXObjectを使ったコードをIEを使ってQUnitから実行する際、確認ダイアログが表示され、[はい]をクリックすれば実行できるようになります。
QUnit.test("Scripting.FileSystemObject", function (assert) {
var fso = new ActiveXObject("Scripting.FileSystemObject");
assert.ok(fso.FileExists("C:\\pagefile.sys"));
});
3. ファイルの先頭から処理が書いてあるからテストできない
WSHはスクリプティング環境であり、ついjsファイルを作ってファイルの先頭からべたべた処理を書いてしまいがちです。しかし、こうやってしまうとどうあがいてもテスト不能になってしまいます。我々はプログラマですので、キチンと責務分割とモジュール化をやっていきましょう。
そのために必要なのは以下の手順です。
- 処理本体は個別のjsファイルにまとめる
- WSHの起動はwsfファイルで行う
処理本体は個別のjsファイルにまとめる
当たり前のことですが、テスト対象のコードは個別のjsファイルに切り出しましょう。こうすることで、QUnitでは対象のコードのみが含まれたjsファイルだけをインポートすればよくなります。
そして、出来ればjsのクラスを使うようにしましょう。そうすることで、function
単位で処理を記述するよりも圧倒的にユニットテストしやすいコードになります。もちろんJScriptにはES2015のようなクラス構文はないので、ES5の方法でやりましょう(参考:【javascript】やさしいクラスの作り方 - Qiita)。
var ConfigLoader = (function() {
var ConfigLoader = function(filepath) {
this.filepath = filepath;
};
ConfigLoader.prototype.load = function() {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var stream = fso.OpenTextFile(this.filepath, ...);
// ... テキストファイルを呼んでitemsオブジェクトに設定 ...
var config = {};
config.hoge = items["hoge"];
return config;
};
return ConfigLoader;
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>QUnit Example</title>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.1.1.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="https://code.jquery.com/qunit/qunit-2.1.1.js"></script>
<script src="../js/ConfigLoader.js"></script>
<script type="text/javascript">
QUnit.test("load config", function (assert) {
var configLoader = new ConfigLoader("テスト用のコンフィグファイルへのパス");
var config = configLoader.load();
assert.equal("bar", config.hoge);
});
</script>
</body>
WSHの起動はwsfファイルで行う
折角個別のjsファイルに処理を分割したのですから、今度はそれを利用するWSHを書かなくてはなりません。そのために利用できるのが、wsfファイルです。
Windows スクリプト ファイル (.wsf) を使用する
wsfファイルではHTMLと同じようにXML形式でWSHスクリプトを書くためのファイルで、これを使うと外部のスクリプトファイルを読み込むことができます。
<?xml version="1.0" encoding="utf-8" ?>
<job>
<script language="JScript" src="./js/ConfigLoader.js"></script>
<script language="JScript">
var configLoader = new ConfigLoader("プロダクション用のコンフィグファイルへのパス");
var config = configLoader.load();
// configを利用するスクリプトを続ける
</script>
</job>
フォルダー構成
最終的には次のようなフォルダー構成になります。
root/
|
+-js/
| |
| +-ConfigLoader.js
| |
| :
|
+-test/
| |
| +-test.html
|
+-execute.wsf
そして、配布時はtest
フォルダーを取り除いて配布すれば一丁上がりです。
おまけ
WScript.Echoの扱い
WSHで簡単にメッセージを表示するためのWScript.Echo
メソッドは当然QUnitからは実行できません。WSHの中で利用したい場合、メッセージ出力用functionを外部から注入するようにすると良いです。
var SomeClass = (function() {
var SomeClass = function(log) {
this.log = log;
};
SomeClass.prototype.hello = function() {
this.log("Hello!");
};
return SomeClass;
})();
var someClass = new SomeClass(function (message) {
alert(message); // ブラウザにメッセージボックスを表示する場合
// console.log(message); // ブラウザのコンソールログに出すならこちら
});
someClass.hello();
var someClass = new SomeClass(function (message) {
WScript.Echo(message);
});
someClass.hello();
WSHの補完
WSHスクリプトをJScriptで書く際、やはりコード補完機能が欲しくなるものです。Visual Studioを使うとWSH-vsdoc.jsを利用することでインテリセンスが有効になり、お勧めです。
[動画あり] Visual Studio の強力な JavaScript インテリセンス機能の活用 - WSHスクリプトエディタに最適! : @jsakamoto