在 AngularJS/Jasmine 中注入 Mock 服务

Posted

技术标签:

【中文标题】在 AngularJS/Jasmine 中注入 Mock 服务【英文标题】:Injecting Mock service in AngularJS/ Jasmine 【发布时间】:2013-08-30 10:26:29 【问题描述】:

我正在使用 jasmine 来测试我用 TypeScript 编写的控制器。我的单元测试是用纯 javascript 编写的。 我在测试控制器时遇到错误,我想在其中注入模拟服务。

这是我的测试的样子:

'use strict';

describe('ConfigCtrl', function()
    var scope, http, location, timeout, $httpBackend, service;

    beforeEach(angular.mock.module('busybee'));

    beforeEach(angular.mock.inject(function($rootScope, $http, $location, $timeout, configService, $controller)

        scope = $rootScope.$new();
        http = $http;
        location = $location;
        timeout = $timeout;
        service = configService;


        $controller('configCtrl', $scope: scope, $http: http, $location: location, $timeout: timeout, configService: service);
    ));

    it('should have text = "constructor"', function()
        expect(true).toBe(true);
    );
); 

我的 app.ts:

module game 
    'use strict';

    var busybee = angular.module('busybee', []);
    busybee.controller('configCtrl', ConfigCtrl);

    busybee.service('configService', ConfigService);
    ...
    ...


还有我的 TypeScript 控制器:

module game 
    'use strict';

    export class ConfigCtrl 

        static $inject: string[] = ['$scope', '$http', '$location', '$timeout', 'configService'];

        constructor($scope: ng.IScope, $http: ng.IHttpService, $location: ng.ILocationService,
            $timeout: ng.ITimeoutService, configService: game.ConfigService)   
            //any code here
        
       

运行 karma 时,出现以下错误:

Chrome 28.0.1500 (Linux) ConfigCtrl should have text = "constructor" FAILED
        TypeError: Cannot read property 'prototype' of undefined
            at Object.instantiate (/home/david/git/busybee2-client/js/libs/angular/angular.min.js:28:283)
            at Object.<anonymous> (/home/david/git/busybee2-client/js/libs/angular/angular.min.js:28:494)
            at Object.d [as invoke] (/home/david/git/busybee2-client/js/libs/angular/angular.min.js:28:174)
            at /home/david/git/busybee2-client/js/libs/angular/angular.min.js:29:339
            at c (/home/david/git/busybee2-client/js/libs/angular/angular.min.js:27:13)
            at Object.d [as invoke] (/home/david/git/busybee2-client/js/libs/angular/angular.min.js:27:147)
            at workFn (/home/david/git/busybee2-client/js/libs/angular/angular-mocks.js:1778:20)
        Error: Declaration Location
            at Object.window.jasmine.window.inject.angular.mock.inject [as inject] (/home/david/git/busybee2-client/js/libs/angular/angular-mocks.js:1764:25)
            at null.<anonymous> (/home/david/git/busybee2-client/js/test/ConfigCtrlSpecs.js:9:29)
            at /home/david/git/busybee2-client/js/test/ConfigCtrlSpecs.js:3:1
Chrome 28.0.1500 (Linux): Executed 1 of 1 (1 FAILED) ERROR (0.329 secs / 0.032 secs)

看来,注入configService 时出现问题,但我不知道为什么。

编辑:添加了一个 jsfiddle http://jsfiddle.net/Q552U/6/

更新:对于 jasmine 来说,将 TypeScript 类的编译后的 javascript 放在不同的文件中似乎是个问题。将 TypeScript 文件编译为单个 .js 文件 (tsc --out dest.js source.ts),对我有用。

【问题讨论】:

你能创建一个带有测试的代码,让每个人都可以看一下吗? (因为使用 TypeScript 的人不多,所以 fiddle 可能有助于调试。) 【参考方案1】:

尝试使用$injector 获取服务。

beforeEach(angular.mock.inject(function($rootScope, $http, $location, $timeout, configService, $controller, $injector)
    scope = $rootScope.$new();
    http = $http;
    location = $location;
    timeout = $timeout;
    service = $injector.get('configService'); //not sure the name, you may try 'ConfigService' as well.

    $controller('configCtrl', $scope: scope, $http: http, $location: location, $timeout: timeout, configService: service);
));

链接到Demo。

【讨论】:

链接到演示会抛出 404【参考方案2】:

zsong 的回答很好,但是可以通过从 angular.mock.inject() 中删除 configService 来进一步更新该代码,因为这不是必需的。

另一种可能性是用 TypeScript 编写测试(并使用 Jasmine 类型定义),这是一种更简洁的方法。在这种情况下,您的测试将如下所示:

/// <reference path="typings/jasmine/jasmine.d.ts" />

describe('ConfigCtrl', () => 
    var configCtrl, scope, http, location, timeout, configServiceFake;

    beforeEach(angular.mock.module('busybee'));

    beforeEach(angular.mock.inject(($rootScope, $http, $location, $timeout, configService) => 
        scope = $rootScope.$new();
        http = $http;
        location = $location;
        timeout = $timeout;
        configServiceFake = configService;

        configCtrl = new game.ConfigCtrl(scope, http, location, timeout, configServiceFake);
    ));

    it('should have text = "constructor"', () => 
        expect(true).toBe(true);
    );
);

【讨论】:

以上是关于在 AngularJS/Jasmine 中注入 Mock 服务的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS Jasmine 测试失败:无法实例化模块

在 Blazor 应用程序中自动注入 javascript

thinkphp5学习总结9-数据流,以及php类的相关术语,请求的属性注入和方法注入

我在项目中运用 IOC(依赖注入)--实战篇

伪静态注入

Sql注入截取字符串常用函数