8

我有以下指令:如何注入模拟依赖性成的角度指令茉莉上噶

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依赖运行?

回答

17

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

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

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

这告诉因缘加载Templates文件夹中的所有HTML文件(如果你的模板是别的地方,把那个文件夹中有)。

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

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

ngHtml2JsPreprocessor: { 
    cacheIdFromPath: function(filepath) { 

     var matches = /^\/(.+\/)*(.+)\.(.+)$/.exec(filepath); 

     return 'templates/' + matches[2] + '.' + matches[3]; 
    } 
}, 

这个接收filepath,并通过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服务返回预定义的字符串。