はじめに

AngularJSはとても便利なフレームワークですが慣れるまではなかなかに時間がかかります。その上Jasmineを用いてテストを書こうとすると「これはAngularJSの記述なの?Jasmineの記述なの?」と混乱することが私は多々ありました。

今回はごく簡単なAngularJSのサービスをJasmineを用いてテストファーストで構築していく過程を例示してそのあたりを整理したいと思います。なお、理解を簡単にするために厳密な説明は行っていません。これをきっかけに雰囲気を掴んでいただいて詳細は皆さん各自で深堀りしていただければと思います。

開発プロセス例

1. 設計

名前を引数として与えると ‘Hello 名前’ という文字列を返すメソッドsay(name)を持ったHelloService サービスを作ってみます。このサービスは MyServices というモジュールに入れることにしましょう。

2. 実装

ではさっそくこの振舞をテストコードに落としこむことから始めてみます。

1. テストスイートの宣言

describe関数はひとまとまりのテストスイートを宣言します。Jasmineの記述です。

// helloSpec.js
+ describe('テスト', function() {
+ });

2. テストケースの宣言

it関数はひとまとまりのテストケースを宣言します。Jasmineの記述です。

// helloSpec.js
describe('テスト', function() {
+    it('HelloService のテスト', function() {
+    });
});

3. テスト対象のサービスをロード

beforeEach関数はあるテスト単位前に必ず実行される処理です。Jasmineの記述です。

module関数はangularモジュールをロードするための宣言です。angular.mock.moduleの記述です。ここではテスト対象のHelloServiceが入っているMyServicesモジュールをロードしています。

injectはサービスの参照を解決します。angular.mock.injectの記述です。ここではHelloServiceサービスの参照を解決します。

// helloSpec.js
describe('テスト', function() {
+    beforeEach(function() {
+        module('MyServices');
+    });

-    it('HelloService のテスト', function() {
-    });
+    it('HelloService のテスト', inject(function(HelloService) {
+    }));
});

ここで以下のようなメッセージが表示されテストが失敗しました。MyServicesモジュールなんてないよ、と言われます。さっそく実装しましょう。

Error: [$injector:modulerr] Failed to instantiate module MyServices due to: Error: [$injector:nomod]   Module 'MyServices' is not available! 

4. サービスを実装

モジュールを定義します。

// myServices.js
+ angular.module('MyServices', [])
+ ;

今度は以下のようにHelloServiceProviderなんて知らないよと言われます。

Error: [$injector:unpr] Unknown provider: HelloServiceProvider <- HelloService

サービスを定義します。

// myServices.js
angular.module('MyServices', [])
+     .service('HelloService', function() {})
;

テストが通りました。対象サービスを実装してロードできましたね。

5. メソッドのテストを宣言

expect, toEqual関数はJasmineの記述です。

ここにあたる部分をコーディングする頻度が最も高くなると思います。もちろんtoEqualだけでなく様々な判定用メソッドが存在します。

// helloSpec.js
describe('MyServicesのテスト', function() {
    beforeEach(function() {
        module('MyServices');
    });
  
    it('HelloService のテスト', inject(function(HelloService) {
+        expect(HelloService.say('Quartet')).toEqual('Hello Quartet');
    }));
});

ここで以下のようなメッセージが表示されテストが失敗しました。

TypeError: undefined is not a function

6. メソッドの実装

// myServices.js
angular.module('MyServices', [])
    .service('HelloService', function() {
+        this.say = function (name) {
+           return 'Hello ' + name;
+        };
    })
;

これで無事にテストが通りました。実装完了です。

最後に

今回はごく簡単なサービスで例示しました。これだけの記法をわかっているだけで基本的なテストは記述できるのではないでしょうか。

しかしAngularJSの特徴であるDIを駆使したサービスのテストには至っていません。そこで次回は依存サービスがある場合の記述について記したいと思います。

ちなみに当エントリのコードはこちらにありますのでご参考まで。

http://plnkr.co/edit/zQ6pj0