AngularJSのバックエンドをモック化してテストする


AngularJSでコントローラからREST APIを叩き、JSONデータを取得することがよくあると思う。バックエンドにモックを使用してJasmineにてテストする方法を備忘録も兼ねて記述しておく。

環境

  • AngularJS 1.3.4
  • Jasmine 2.1.2

APIアクセスにモックを使用する

テストの対象となるコントローラは次のような感じ。

📄posts_ctrl.coffee
angular.module('App')
  .controller 'PostsCtrl', ($scope, Post) ->
    $scope.posts = Post.query()

よくある感じ。Postモデルも典型的で、REST APIにてバックエンドからJSONを取得する。

📄post.coffee
angular.module('App')
  .factory 'Post', ($resource) ->
    $resource '/api/posts'

このコントローラをJasmineでユニットテストするのだが、このバックエンドへのアクセスをモック化する。$httpBackendをインジェクションすればよい。

📄posts_ctrl_spec.coffee
'use strict'

describe 'PostsCtrl', ->
  beforeEach ->
    module 'App'

  beforeEach inject ($httpBackend, $controller, $rootScope, Post) ->
    @$scope = $rootScope.$new()
    # REST APIアクセスをモック化する
    $httpBackend.when('GET', '/api/posts').respond([{ mock: 'spec' }])
    $controller('PostsCtrl', { $scope: @$scope, Post: Post })
    $httpBackend.flush()

  it "assigns posts to $scope", ->
    expect(@$scope.posts).toEqual([{ mock: 'spec' }])

よしよし、これでテストに成功す・・・・るはずが失敗してしまった。

📄posts_ctrl_spec.coffee
PhantomJS 1.9.8 (Mac OS X) Jasmine__TopLevel__Suite PickupCtrl assigns data tweets to $scope FAILED
	Expected [ { mock: 'spec' } ] to equal [ { mock: 'spec' } ].
	Error: Expected [ { mock: 'spec' } ] to equal [ { mock: 'spec' } ].
...

{ mock: 'spec' }って、バッチリ一致してるじゃん・・と思いきや、JasmineのtoEqualメソッドはJSON同士の比較はできないらしい。ちぇっ、気が利かないなあ。

しょうがないので、angularのequalsメソッドを使用するように、カスタムマッチャを追加する。

describe 'PickupCtrl', ->
  # カスタムマッチャ
  beforeEach ->
    jasmine.addMatchers
      toEqualData: (util, customEqualityTesters) ->
        compare: (actual, expected) ->
          { pass: angular.equals(actual, expected) }
...
  it "assigns data tweets to $scope", ->
    expect(@$scope.dataTweets).toEqualData([{ mock: 'spec' }])

toEqualDataという少々ダサネームのマッチャを追加した。これでテストに成功するはず。

ちょっとリファクタリング

このままでもよいのだが、この手のカスタムマッチャは色んなテストから使用すると思うので、別ファイルに定義しておきたい。spec_helper.coffeeというファイルを作成して、そのファイルにまとめて記述しておこう。

📄spec_helper.coffee
# カスタムマッチャ
beforeEach ->
  jasmine.addMatchers
    toEqualData: (util, customEqualityTesters) ->
      compare: (actual, expected) ->
        { pass: angular.equals(actual, expected) }

# モジュール
beforeEach ->
  module 'App'

1モジュールで構成するアプリなら、上記のようにspec_helperで読み込んじゃってもいいと思う。

requireを記述すれば、テストコードが少しスッキリするはずだ。

📄posts_ctrl_spec.coffee
#= require spec_helper

'use strict'

describe 'PickupCtrl', ->
  beforeEach inject ($httpBackend, $controller, $rootScope, Post) ->
    @$scope = $rootScope.$new()
    $httpBackend.when('GET', '/api/posts').respond([{ mock: 'spec' }])
    $controller('PostsCtrl', { $scope: @$scope, Post: Post })
    $httpBackend.flush()

  it "assigns posts to $scope", ->
    expect(@$scope.posts).toEqualData([{ mock: 'spec' }])

うむ、今日のところはコレで勘弁しといてやろう!

参考サイト

関連する記事


コメントする

メールアドレスが公開されることはありません。

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください