【问题标题】:How do I inject a mock dependency into an angular directive with Jasmine on Karma如何在 Karma 上使用 Jasmine 将模拟依赖项注入角度指令
【发布时间】:2014-01-08 18:55:27
【问题描述】:

我有以下指令:

function TopLevelMenuDirective ($userDetails, $configuration) {
    return {
        restrict:'A',
        templateUrl: staticFilesUri + 'templates/TopLevelMenu.Template.html', 
        scope: {
            activeTab: '='
        },
        link: function (scope, element, attributes) {
            var userDetails = $userDetails;
            if ($userDetails) {
                scope.user = {
                    name: userDetails.name ? userDetails.name : 'KoBoForm User',
                    avatar: userDetails.gravatar ? userDetails.gravatar: (staticFilesUri + '/img/avatars/example-photo.jpg')
                };
            } else {
                scope.user = {
                    name: 'KoBoForm User',
                    avatar: staticFilesUri + '/img/avatars/example-photo.jpg'
                }
            }

            scope.sections = $configuration.sections();

            scope.isActive = function (name) {
                return name === scope.activeTab ? 'is-active' : '';
            }
        }
    }
}

我想模拟依赖项以使用单元测试已知的值对不同的代码路径进行单元测试。我有以下示例单元测试:

it('should set $scope.user to values passed by $userDetails', 
    inject(function($compile) {
        var element = '<div top-level-menu></div>';
        element = $compile(element)($scope);
        $scope.$apply();

        expect(element.isolateScope().user.name).toBe('test name');
        expect(element.isolateScope().user.avatar).toBe('test avatar');
    }
));

这给了我两个问题。

首先,由于模板位于外部文件中,因此在加载时会尝试获取它并出错,因为找不到该文件,这是合乎逻辑的,因为它处于测试环境而不是实际服务器中。

其次,没有明显的方法可以模拟通过构造函数注入指令的依赖项。在测试控制器时,您可以使用 $controller 服务,但由于指令是通过编译具有传递范围的 html 标记间接实例化的,因此无法直接实例化它(例如,没有类似的 $directive)。这阻碍了我将$userDetails.name$userDetails.gravatar 分别设置为'test name''test avatar'

如何让指令正确编译并使用自定义 $userDetails 依赖项运行?

【问题讨论】:

    标签: unit-testing angularjs dependency-injection angularjs-directive karma-runner


    【解决方案1】:

    要加载模板文件,您必须在 karma 中配置karma-ng-html2js-preprocessor

    首先,访问this page 并按照安装说明进行操作。然后,您需要在 karma.config.js 文件中添加几个条目:

    files: [
        'templates/*.html'
    ],
    

    这告诉 karma 加载模板文件夹中的所有 html 文件(如果您的模板在其他地方,则将该文件夹放在那里)。

    preprocessors: { '**/*.html': 'ng-html2js' },
    

    这告诉 karma 将所有 html 文件通过 ng-html2js 预处理器,然后将它们转换为角度模块,将模板放入 $templateCache 服务。这样,当$httpBackend 查询模板的“服务器”时,它会被模板缓存拦截并返回正确的 html。这里一切正常,除了模板的 URL:它必须匹配指令中的 templateUrl 属性,并且默认情况下 ng-html2js 将完整路径作为 uri 传递。所以我们需要转换这个值:

    ngHtml2JsPreprocessor: {
        cacheIdFromPath: function(filepath) {
    
            var matches = /^\/(.+\/)*(.+)\.(.+)$/.exec(filepath);
    
            return 'templates/' + matches[2] + '.' + matches[3];
        }
    },
    

    这将接收filepath 并将其传递给regular expression,该regular expression 将路径、文件名和扩展名提取到一个数组中。然后,您将 'templates/ 添加到文件名和扩展名之前,您将获得预期的 uri。

    完成所有这些后,使模板可用是在运行测试之前加载模块的问题:

    beforeEach(module('templates/TopLevelMenu.Template.html'));
    

    请记住,module 是位于angular-mocks.js 中的外部服务。

    要在指令中注入自定义服务,您需要覆盖服务的提供者:

    beforeEach(module(function ($provide) {
        $provide.provider('$userDetails', function () { 
            this.$get = function () {
                return {
                    name: 'test name', 
                    gravatar: 'test avatar'
                };
            }
        });
    }));
    

    $provide 是为您的提供商提供的服务。因此,如果您想注入模拟依赖项,请在此处覆盖提供程序。

    在测试之前执行该代码,您将拥有一个模拟 $userDetails 服务,该服务返回您的预定义字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      • 2013-02-23
      • 2021-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多