【问题标题】:AngularJS unit test controller with only private functions只有私有功能的 AngularJS 单元测试控制器
【发布时间】:2015-09-27 12:25:20
【问题描述】:

我有以下只有私有功能的控制器。我正在努力测试这个控制器。我应该测试它是否正在执行 $emit,因为 ImageService 已经过测试?在这种情况下如何测试 $emit ?或者我应该测试它是否正在调用 ImageService.fetchImageStacks 方法?这种情况下如何触发init函数?

(function (angular, global, undefined) {
'use strict';

var ImageController = {};

ImageController.$inject = [
    '$rootScope',
    '$log',
    'ImageService'
];

ImageController = function (
    $rootScope,
    $log,
    ImageService
) {

    var getImageStacks = function() {
        ImageService
            .fetchImageStacks()
            .success(function (result) {
                ImageService.setImageStacks(result);
                $rootScope.$emit('rootScope:imageStacksUpdated', result);
            })
            .error(function (){
                $log.error('Failed to get imageStackInfo file.');
            });
    };

    var init = function () {
        getImageStacks();
    };

    init();

    return {
        getImageStacks: getImageStacks
    };
}

angular.module('myApp')
    .controller('ImageController', ImageController);

})(angular, this);

【问题讨论】:

    标签: angularjs unit-testing


    【解决方案1】:

    您不应该测试对外部世界不可用的私有/内部方法(imho)。

    关于该主题的一些资源(赞成和反对):


    话虽如此,您正在控制器上公开getImageStacks - 所以它不是私有方法。如果您要在测试套件中注销实例化控制器的结果,您应该会看到以下内容:

    { getImageStacks: function }

    (在您的情况下,init() 只是 getImageStacks 的别名(也就是说,不需要 init 方法 - 您可以调用 getImageStacks 并完成它))。

    不管怎样,写一些测试;

    首先,您应该将stub 替换为ImageService,因为我们对所述服务的内部实现不感兴趣,我们只对从控制器到服务的通信感兴趣。 sinonjs 是一个很棒的存根/模拟/间谍库 - 明白了,你不会后悔的。

    beforeEach 我建议你这样做:

    // Stub out the ImageService
    var ImageService = {
      fetchImageStacks: sinon.stub(),
      setImageStacks: sinon.stub()
    };
    
    var $scope, instantiateController;
    
    beforeEach(function () {
      // Override the ImageService residing in 'your_module_name'
      module('your_module_name', function ($provide) {
        $provide.value('ImageService', ImageService);
      });
    
      // Setup a method for instantiating your controller on a per-spec basis.
      instantiateController = inject(function ($rootScope, $controller, $injector) {
        ctrl = $controller('ImageController', {
          $scope: $rootScope.$new(),
          // Inject the stubbed out ImageService.
          ImageService: $injector.get('ImageService')
        });
      });
    });
    

    现在您有一个已存根的 ImageService 来测试调用,以及一个使用传递给它的依赖项实例化您的控制器的方法。

    您可以运行的一些示例规范;

    it('calls the ImageService.fetchImageStacks method on init', function () {
      instantiateController();
      expect(ImageService.fetchImageStacks).to.have.been.calledOnce;
    });
    
    it('calls the ImageService.setImageStacks on success', inject(function ($q, $timeout) {
      ImageService.getImageStacks.returns($q.when('value'));
      instantiateController();
      $timeout.flush();
      expect(ImageService.setImageStacks).to.have.been.calledOnce.and.calledWith('value');
    }));
    

    我希望这足以回答您的问题;

    • 如果/何时应该/不应该测试内部实现。
    • 如何测试控制器的初始化。
    • 如何测试注入服务的方法。

    【讨论】:

    • 很高兴能为您提供帮助!
    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 2016-01-27
    • 2016-07-25
    • 1970-01-01
    • 2014-04-12
    • 2015-11-09
    • 2019-08-24
    • 1970-01-01
    相关资源
    最近更新 更多