【问题标题】:Jasmine controller and mock service not being injected into the unit testsJasmine 控制器和模拟服务未注入单元测试
【发布时间】:2016-01-09 01:13:10
【问题描述】:

这是我的 test_sp.js 文件:

describe('Controller: MainCtrl', function() {
    var ctrl, mockBaseService;

    beforeEach(function() {

        mockBaseService = {
            cerrorMessages: 'whatever',
            sp: "[{'spName': 'Test'}]",
            // Calls back if errors.
            add: jasmine.createSpyObj('BaseService.add', ['sp']),
            logout: jasmine.createSpy('BaseService.logout'),
            // Calls back if errors.
            fetch: jasmine.createSpyObj('BaseService.fetch', ['selfsp'])
        };

        mockBaseService.add.sp.and.callFake(function(something, cb) {
            cb(); // execute the callback immediately
        });

        mockBaseService.fetch.selfsp.and.callFake(function(something, cb) {
            cb(); // execute the callback immediately
        });

        module('SpPageApp');

        // Let ctrl = MainCtrl and override BaseService in MainCtrl to be
        // the mockBaseService above.
        inject(function($controller) {
            ctrl = $controller('MainCtrl', {
                BaseService: mockBaseService
            });
        });
    });

    it('should have an add function', function() {
        expect(ctrl.add).toBeDefined();
    });
});

这是我的 karma 配置文件的 file 数组:

files: [
  '../angular.js',
  'node_modules/angular-mocks/angular-mocks.js',
  '../w.js',
  '../sp.js',
  '../s.js',
  '../base.js',
  'tests/test_sp.js',
],

最后,这是我的sp.js,它有SpPageApp 模块:

angular.module("SpPageApp", ["BaseApp"])
    .controller("MainCtrl", ["$http", "$window", "BaseService", function($http, $window, BaseService) {

        var self = this;

        BaseService.fetch.selfsp(function() {
            self.sp = BaseService.sp;
            self.cerrorMessages = BaseService.cerrorMessages;
        });

        self.add = function() {
            BaseService.add.sp(self.sp, function() {
                self.cerrorMessages = BaseService.cerrorMessages;
            });
        };

        self.logoutUser = function() {
            BaseService.logout();
        };

    }]);

我使用karma start 来测试代码,我收到一条错误消息:

Chromium 47.0.2526 (Ubuntu 0.0.0) Controller: MainsCtrl should have an add function FAILED
    TypeError: Cannot read property 'and' of undefined
        at Object.<anonymous> (/home/a/Documents/CMS/CMSApp/static/js/karma/tests/test_sp.js:20:45)
    TypeError: Cannot read property 'add' of undefined
        at Object.<anonymous> (/home/a/Documents/CMS/CMSApp/static/js/karma/tests/test_sp.js:36:20)
Chromium 47.0.2526 (Ubuntu 0.0.0): Executed 1 of 1 (1 FAILED) (0 secs / 0.006 secChromium 47.0.2526 (Ubuntu 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.034 secs / 0.006 secs)

错误说无法读取已定义的属性add,并指向这一行:

expect(ctrl.add).toBeDefined();

并且它无法读取未定义的属性and,并指向这一行:

mockBaseService.fetch.selfsp.and.callFake(function(something, cb) {

Jasmine 怎么会说 ctrlmockBaseService.fetch.selfsp 在代码中定义时都是未定义的?

编辑:对于它的价值,w.jssp.jss.js(正在加载业力)都有名为 MainCtrl 的控制器,但每个控制器都在它自己的文件和 AngularJS 模块中(以及 @987654340 @,我只加载SpPageApp模块)。

【问题讨论】:

  • 既然已经模拟了服务,为什么还要称其为假货?只需调用模拟服务即可。
  • @DavidL Phil 在此处发布的答案:stackoverflow.com/questions/34623322/… 在模拟服务时调用了一个假的,所以我决定也使用它(我是 Jasmine 的新手,所以我认为 Phil 所做的是最佳实践)。我相信这是因为ctrl.add() 将一个函数传递给BaseService.add.sp 应该被回调。
  • 他的答案的替代方案如下: mockBaseService = { cerrorMessages: 'whatever', add: function() { // 不管 add 函数应该做什么 } };在这种情况下,您无需使用 spy 对象创建额外的开销,而是简单地覆盖服务并将覆盖的模拟服务注入您的控制器,因为您关心的只是测试控制器,而不是服务。

标签: angularjs jasmine karma-jasmine


【解决方案1】:

正如我在 cmets 中提到的,您可以改为直接模拟服务并避免使用间谍。此外,您可以告诉 $injector 使用您的 mock 服务而不是真正的实现,从而允许您在解析服务时直接注入 mock,从而替代您的正常实现。

假设你的 BaseService 存在于 SpPageApp 模块中:

mockBaseService = {
    cerrorMessages: 'whatever',
    sp: "[{'spName': 'Test'}]",
    add: function (something, cb) { cb() },
    ...
};

module('BaseApp', function($provide) {
    $provide.value('BaseService', mockBaseService);
});

module('SpPageApp');

inject(function($controller) {
    ctrl = $controller('MainCtrl', {
    });
});

要对其进行测试,如 cmets 中所述,您仍然需要使用 spy 来确保它被成功调用。

it('should have an add function', function() {
    spyOn(mockBaseService, 'add');        
    ctrl.add();
    expect(mockBaseService.add).toHaveBeenCalled();
});

【讨论】:

  • 但是 mockBaseService 需要有一个mockBaseService.add.sp() 函数(见我的原帖)。在您提供的答案中,mockBaseService.add.sp() 不存在,对吗?或者我错过了什么.. 另外,我想创建另一个测试来测试ctrl.add() 是否调用mockBaseService.add.sp()(即`expect(mockBaseService.add.sp).toHaveBeenCalledWith(ctrl.post, jasmine.any(Function ));` .. 是否可以在不使用间谍的情况下查看函数是否被调用?
  • 最后,BaseService 存在于base.js 中的BaseApp 模块中。BaseApp 模块提供给SpPageApp,如下所示:angular.module("SpPageApp", ["BaseApp"])base.js 加载到 karma 配置文件中,如下所示:files: [ '../base.js', ],
  • 你可以修改你的模拟服务签名来调用你想要调用的任何东西。因此,您可以定义 add 和 add.sp。没有什么能阻止你这样做。我已经更新了答案以匹配您的模块。此外,如果您愿意,您仍然可以监视您的模拟服务。但是,与在模拟服务中使用模拟间谍对象相比,这可以更清晰地分离关注点。
  • 好的,谢谢。为了澄清起见,我想创建另一个测试来测试ctrl.add() 是否调用mockBaseService.add.sp()(即expect(mockBaseService.add.sp).toHaveBeenCalledWith(ctrl.post, jasmine.any(Function)); .. 我如何测试mockBaseService.add.sp() 是否在不使用间谍的情况下被调用(因为这是其中一个我最初使用间谍的原因)。
  • 我会小心的。您是在测试您的服务还是您的控制器?您开始模糊界限,这听起来像是服务测试,应该是一组单独的测试。除非您可以访问 add.sp() 的输出,否则您无法确定它是在没有间谍的情况下被调用的。澄清一下,在逐个测试的基础上设置间谍来验证呼叫并没有错。我最初的观点是,不需要监视每个调用中的每个属性,而是您可以在需要时监视所需的内容。
猜你喜欢
  • 2014-11-22
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
  • 2014-01-16
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 2015-09-20
相关资源
最近更新 更多