web-dev-qa-db-ja.com

Angular単体テストコントローラー-コントローラー内のモックサービス

次のような状況です。

controller.js

controller('PublishersCtrl',['$scope','APIService','$timeout', function($scope,APIService,$timeout) {

    APIService.get_publisher_list().then(function(data){

            });
 }));

controllerSpec.js

'use strict';

describe('controllers', function(){
    var scope, ctrl, timeout;
    beforeEach(module('controllers'));
    beforeEach(inject(function($rootScope, $controller) {
        scope = $rootScope.$new(); // this is what you missed out
        timeout = {};
        controller = $controller('PublishersCtrl', {
            $scope: scope,
            APIService: APIService,
            $timeout: timeout
        });
    }));

    it('should have scope variable equals number', function() {
      expect(scope.number).toBe(3);
    });
});

エラー:

 TypeError: Object #<Object> has no method 'get_publisher_list'

私もこのようなものを試しましたが、うまくいきませんでした:

describe('controllers', function(){
    var scope, ctrl, timeout,APIService;
    beforeEach(module('controllers'));

    beforeEach(module(function($provide) {
    var service = { 
        get_publisher_list: function () {
           return true;
        }
    };

    $provide.value('APIService', service);
    }));

    beforeEach(inject(function($rootScope, $controller) {
        scope = $rootScope.$new(); 
        timeout = {};
        controller = $controller('PublishersCtrl', {
            $scope: scope,
            APIService: APIService,
            $timeout: timeout
        }
        );
    }));

    it('should have scope variable equals number', function() {
      spyOn(service, 'APIService');
      scope.get_publisher_list();
      expect(scope.number).toBe(3);
    });
});

どうすればこれを解決できますか?助言がありますか?

20
Liad Livnat

2つの方法があります(確かにそれ以上)。

この種のサービスの想像(工場であるかどうかは関係ありません):

_app.service('foo', function() {
  this.fn = function() {
    return "Foo";
  };
});
_

このコントローラーで:

_app.controller('MainCtrl', function($scope, foo) {
  $scope.bar = foo.fn();
});
_

1つの方法は、使用するメソッドを使用してオブジェクトを作成し、それらをスパイすることです。

_foo = {
  fn: function() {}
};

spyOn(foo, 'fn').andReturn("Foo");
_

次に、そのfooをdepとしてコントローラーに渡します。サービスを注入する必要はありません。うまくいきます。

もう1つの方法は、サービスをモックして、モックしたものを挿入することです。

_beforeEach(module('app', function($provide) {
  var foo = {
    fn: function() {}
  };

  spyOn(foo, 'fn').andReturn('Foo');
  $provide.value('foo', foo);
}));
_

fooを注入すると、これが注入されます。

こちらをご覧ください: http://plnkr.co/edit/WvUIrtqMDvy1nMtCYAfo?p=preview

ジャスミン2.0:

答えを出すのに苦労している人のために、

jasmine 2.0以降、andReturn()and.returnValue()になりました

したがって、たとえば上記のプランカーの最初のテストでは、次のようになります。

_describe('controller: MainCtrl', function() {
  var ctrl, foo, $scope;

  beforeEach(module('app'));

  beforeEach(inject(function($rootScope, $controller) {
    foo = {
      fn: function() {}
    };

    spyOn(foo, 'fn').and.returnValue("Foo"); // <----------- HERE

    $scope = $rootScope.$new();

    ctrl = $controller('MainCtrl', {$scope: $scope , foo: foo });
  }));

  it('Should call foo fn', function() {
    expect($scope.bar).toBe('Foo');
  });

});
_

(出典: Rvandersteen

37
Jesus Rodriguez