【问题标题】:Angular unit-test controllers - mocking service inside controllerAngular 单元测试控制器 - 控制器内的模拟服务
【发布时间】:2014-01-16 16:02:01
【问题描述】:

我有以下情况:

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);
    });
});

我该如何解决这个问题?有什么建议吗?

【问题讨论】:

  • 你的controller.js 语法错误
  • @dcodesmith 是的,我缩短了它,因为真正重要的是函数调用,忽略所有其余 APIService.get_publisher_list()
  • 除非您将严格的声明放入函数中,否则我拒绝提供帮助:pbs.twimg.com/media/BcMF93NCQAAXKhW.jpg

标签: unit-testing angularjs karma-runner karma-jasmine


【解决方案1】:

有两种方法(或更多肯定)。

想象一下这种服务(如果是工厂没关系):

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

使用此控制器:

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

一种方法是使用您将使用的方法创建一个对象并监视它们:

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

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

然后你将 foo 作为一个 dep 传递给控制器​​。无需注入服务。这会起作用的。

另一种方法是模拟服务并注入模拟的服务:

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()

例如,在上面 plunker 的第一个测试中:

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

【讨论】:

  • 如果我只想确保 fn 函数被调用一次怎么办?
  • @Jackie 间谍有 .calls.count() 你可以用来验证它是 1。
猜你喜欢
  • 2014-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-27
  • 1970-01-01
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多