SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

AngularJSではじめるJavaScriptフレームワーク開発スタイル

AngularJSのサービス開発とユニットテスト ~テストの流れを把握して保守性の高いコードにしよう

AngularJSではじめるJavaScriptフレームワーク開発スタイル 第5回

ユニットテストをしてみよう

 AngularJSはDOM要素とプログラムを切り離して実行が可能なために、比較的テストが行いやすいフレームワークになっています。実際にどのような流れでテストが行えるのかを紹介します。

Jasmineをつかってみる

 Jasmineは、AngularJSのドキュメントでも、紹介されているユニットテストのフレームワークです。

 もう一つ、karmaというテストフレームワークも紹介されていますが、こちらは、コマンドラインからJavaScriptを実行する事ができる環境で、Jasmineなどと組み合わせてテストを行うことができます。

 ただし、今回はブラウザだけで実行が可能なようにJasmineのみでのテスト方法を紹介します。

AngularJS-mockとJasmineをダウンロードする

 AngularJSでユニットテストをする際には、ngMockという別のモジュールが必要になります。ngMockは「angular-mocks.js」というプログラムに含まれていますので、ダウンロードサイト から別途ダウンロードします。

 または、連載の初回で紹介したAngularJSのzipファイルにも含まれていますので、そちらを利用しても問題ありません。今回はサンプルプログラムとAngularのバージョンを合わせるために1.2系を利用しています。

 続いて、Jasmineはgithub上のjasmine-standalone-x.y.z.zipファイルをダウンロードします。今回は、2.1.3のバージョンであるjasmine-standalone-2.1.3.zipを使用します。

テストプログラムを用意する

 プロジェクトの配下にtestフォルダを作成し、先ほどダウンロードしたjasmine-standalone-2.1.3.zipファイルを、その配下に解凍します。できあがったフォルダ構造は図1のようになります。また、テストサンプルを含んだHTMLファイル(SpecRunner.html)ができあがりますので、リスト6のように修正してテストを実施します。

図1 フォルダ構造
図1 フォルダ構造
リスト6 SpecRunner.htmlの抜粋
<script src="lib/jasmine-2.1.3/jasmine.js"></script>
<script src="lib/jasmine-2.1.3/jasmine-html.js"></script>
<script src="lib/jasmine-2.1.3/boot.js"></script>

<!-- (1)Angularとアプリで使用するソースを記述 -->
<script src="../vendors/angular.js"></script>
<script src="../vendors/angular-mocks.js"></script>
<script src="../js/app.js"></script>
<script src="../js/controllers.js"></script>
<script src="../js/services.js"></script>
<script src="../js/service/ArrayItems.js"></script>

<!-- (2)実際のテストコード -->
<script src="spec/TodoSpec.js"></script>

 (1)ではAngularJSのコードとngMock、そして、これまで作成したコードを指定します。(2)はテストをするためのコードで、解凍するとサンプルとして存在するPlayerSpec.jsなどを参考に記述していきます。

テストプログラムの概要

 jasmineを使ったユニットテストのためのプログラムは、リスト7のようになります。describeメソッドで、テストをするグループのまとまりを作成します。続いて、beforeEachメソッドを使ってテストを実施する前の事前準備を記述します。beforeEachメソッドはいくつ記述しても問題ありません。続いて、itメソッドを使って、テストを実施します。このような構造をテストの個数分だけ記述していきます。Jasmineの詳しい使い方は他の記事にもあるので、そちらを参照するとよりわかります。

リスト7 jasmineのテストプログラムの構造
describe("テストの説明",function(){ // (1) 大きなテストのくくり
    beforeEach(function(){
        //  事前準備
    });
    it("テスト項目1",function(){
        //  実際のテスト処理を記述
        //  アサーションメソッドを使って値をチェック
        expect(1).toEqual(1);
    })
});

 AngularJSのモジュールやコントローラを使うには事前準備をする必要があります。

AngularJSでのテストコード

 サンプルで作成したpageController内に定義したメソッド(リスト8)のテストコードを作成していきます。

リスト8 pageControllerのshowMessage関数
$scope.showMessage = function(msg){
    $scope.message = msg;
    $timeout(function(){
        $scope.message.show = false;
    },3000);
};

 AngularJSのコードを実行するためには、モジュールの作成やテストするコントローラの作成など、おおよそ同じような前処理を記述する必要があります。実際にテストコードを記述したコードがリスト9です。

リスト9 TodoSpec.jsの抜粋
describe("ToDoサンプルのテスト", function () {
    // (1)DIで設定する変数
    var $rootScope, $timeout, $controller, Items;
    var controller, $scope;
    // (2)モジュールをロードする
    beforeEach(module('todoModule'));
    // (3)DIを用いて、必要なサービスを設定する
    beforeEach(inject(function (_$controller_, _$rootScope_, _$timeout_, _Items_) {
        $rootScope = _$rootScope_;
        $controller = _$controller_;
        Items = _Items_;
        $timeout = _$timeout_;

        $scope = $rootScope.$new(); // (4)$scope変数を$rootScopeから作成
        controller = $controller('pageController', {
            $scope: $scope, $timeout: $timeout, Items: Items
        });  // (5)pageControllerを作成
    }));
    it('messageのテスト', function () {
        var msg = {
            type: 'alert-warning',
            text: '削除しました',
            show: true
        };
        // (6)pageController内の$scope.showMessageを実行
        $scope.showMessage(msg);
        // (7)show == true で有ることを確認
        expect($scope.message.show).toEqual(true);
        // (8)$timeoutに予約されている処理を強制的に実行
        $timeout.flush();
        // (9)show == false に変更されている事を確認
        expect($scope.message.show).toEqual(false);
    });
});

 (1)では、DI機能を使って取得したオブジェクトを保存するための変数を用意します。続いて、(2)ではmodule関数を使ってtodoModuleをロードしています。(3)では(5)で呼び出すpageControllerの引数に必要なサービスを設定します。

 inject関数はコントローラを定義するときの引数名からサービスを設定することと同じようなことを行います。ただし、$scopeオブジェクトだけは(4)のように$rootScopeから$newメソッドを作って作成します。実際にテストを行う処理が(6)~(9)のようになります。

 $timeout内で3秒後に値を変える処理がありますが、(8)のように$timeout.flush()を使用すると即時に処理が実行されるので、実際の時間を待たずに処理が確認できます。

 injectメソッドはAngularJSのDI(依存性注入)機能と同じように使えます。

 従って、$rootScopeを使いたい場合に、引数に$rootScopeを記述すると、自動的に設定されます。しかしながら、変数名が同じだとテストコードを記述しにくいなどの理由から、_$rootScope_のように変数名の両側に"_"(アンダースコア)を記述した方式を推奨しています。

最後に

 これまでサンプルコードを通じてAngularJSの開発からテストまでの流れを紹介してきました。多少難しく感じる部分があるかと思いますが、現時点ではあまり気にする必要はありません。何回かコードを記述していると同じような記述方法になっていると気がつくはずです。そうなれば、後はリファレンスを見ながらコードが記述できるようになります。次回からは、ディレクティブなど機能毎の使い方を説明します。

参考資料

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
AngularJSではじめるJavaScriptフレームワーク開発スタイル連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクト について> 有限会社 WINGSプロジェクト が運営する、テクニカル執筆コミュニティ(代表 ...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8424 2015/02/06 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー