javascript - AngularJS 单元测试 - 多个模拟和提供者

标签 javascript angularjs unit-testing mocking

我开始为我的 Angular 应用程序进行单元测试,我对如何实际构建测试文件夹有一些疑问。我基本上使用了 yeoman Angular 生成器,所以它预配置了 Jasmine 和 Karma。

这是我要测试的场景...

我有一个“PageHeaderDirective”,它显示用户名和电子邮件(如欢迎消息)以及注销链接。页眉指令的代码无关紧要,但我确实需要从后端点击“/user”端点以获取用户的详细信息。这是注入(inject) PageHeaderDirective 的 UserService 代码:

/**
 * @ngdoc function
 * @name Common.service.UserService
 * @description
 * Service to retrieve a {@link User} from the backend.
 */
(function () {
    'use strict';

    angular.module('Common').service('UserService', UserService);

    UserService.$inject = ['User', 'Restangular'];

    /**
     * User service function.
     * @param User The {@link User} provider.
     * @param Restangular The restangular provider.
     */
    function UserService(User, Restangular) {
        var userPromise;

        return {
            getUser: getUser
        };

        /**
         * Retrieves a {@link User} instance from the /user endpoint.
         * @returns A promise to be resolved with a {@link User} instance.
         */
        function getUser() {
            if(!userPromise) {
                userPromise = Restangular.one('user').get().then(function(data) {
                    return User.factory(data);
                });
            }
            return userPromise;
        }
    }

})();

这是一个非常简单的 PageHeaderDirective 测试:

describe('Pageheader Tests', function() {
    'use strict';

    var scope;
    var element;

    beforeEach(module('templates'));
    beforeEach(module('Common'));

    beforeEach(inject(function(_$rootScope_, $compile) {
        scope = _$rootScope_.$new();

        scope.message = 'Test message';

        element = '<ft-page-header message="message" page="home"></ft-page-header>';
        element = $compile(element)(scope);
        scope.$digest();
    }));

    it('should render a page header with the logo and username', function() {
        expect(element.find('.logo-text').length).toBe(1);
        var isolateScope = element.isolateScope();
        expect(isolateScope.name).toBe('test');
    });
});

现在,您可能会说,我收到了一个未知的提供者错误“Unknown provider: RestangularProvider <- Restangular <- UserService <- pageHeaderDirective”,因为我没有将它注入(inject)到测试中。

我读到你可以做一些像 beforeEach(function(){ module(function($provide) { $provide.service('UserService', function() { ... }})} ); 在每个测试文件中,但我真的不想在指令/ Controller 使用 UserService 时这样做。如何从每个测试文件中分离出该部分并将其放入自己的“UserService .mock.js”文件?如果可能的话,我该如何将“UserService.mock.js”注入(inject)到我的测试中?

其次,我还将 Restangular 注入(inject) PageHeaderDirective 以注销用户 (Restangular.one('logout').get().then...)。我如何模拟这个(我不想调用 API 端点)?

最后,如果我要注入(inject)其他提供程序($document、$localStorage、$window),我是否也需要将所有这些提供程序都注入(inject)到测试中?如果是,怎么办?

谢谢!

最佳答案

如果有人想做我所做的事情(将你的模拟分成不同的文件,这样你就不需要复制粘贴很多东西),这是我发现的。

// /test/mock/UserService.mock.js
(function() {
    "use strict";

    angular.module('mocks.Common').service('UserService', mock);

    mock.$inject = ['$q', 'User'];

    function mock($q, User) {
        return {
            getUser : getUser
        };

        function getUser() {
            return $q.when(User.factory({
                firstName: 'test',
                email: 'test@gmail.com',
                id: 1
            }));
        }
    }

})();

所以首先,您需要确保您的模块(在本例中我创建了“mocks.Common”)已创建。在一个单独的文件中,我放置了这一行:angular.module('mocks.Common', []); 这将创建我的“mocks.Common”模块。然后,我创建了一个名为“UserService”的模拟,并使用 $q 返回带有一些虚拟数据的 promise 。 User.factory 部分只是我的 Common 模块中的真实应用程序中的一个工厂函数。

一旦您拥有上述模拟的“UserService”,请确保在测试设置期间以正确的顺序注入(inject)模块。像这样:

module('app');
module('templates');
module('mocks.Common');

现在,当我的测试运行时,PageHeaderDirective 将使用模拟的“UserService”而不是真实的!

至于我的第二个问题:我还没有真正做到这一点,但我相信我将能够使用 $httpBackend 来测试任何 Restangular 功能。

第三,我发现如果您在所有测试中只运行 module('appName'),您应该会自动获取所有必需的依赖项。例如,这是我对整个应用程序的模块定义:

angular.module('app', [
    'Common',
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'ngTouch',
    'ngDialog',
    'ngStorage',
    'lodash',
    'smart-table',
    'rhombus',
    'helpers',
    'restangular',
    'moment',
    'cgBusy',
    'duScroll'
])

因此,当我执行 module('app') 时,我会在我的测试中自动获取所有这些依赖项(请注意我的应用程序配置中的“通用”依赖项)。

关于javascript - AngularJS 单元测试 - 多个模拟和提供者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30739183/

相关文章:

javascript - 带有对象数组的 ngOptions 不设置第一个值

Javascript - 将一个数组分配给另一个数组的键

javascript - 左右定位的奇怪结果不同

javascript - ExtJs 或 DOJO 与 JQuery 有何不同

javascript - 在 Google Places AutoComplete 中添加自定义默认位置

javascript - 如何在可用时使 Angular.js 应用离线并与服务器同步

asp.net - 测试如何与 ASP.NET MVC/WEBFORMS Silverlight MVVM 相关

javascript - 创建类似于 Mocha JavaScript 的单元测试框架

javascript - jquery each 在循环时无法正常工作

java - Spring 模拟 MVC 单元测试和 Controller 建议