dnc*_*253 48 unit-testing angularjs angularjs-directive
在我们的应用程序中,我们有几层嵌套指令.我正在尝试为顶级指令编写一些单元测试.我已经嘲笑了指令本身需要的东西,但现在我遇到了来自低级指令的错误.在我对顶级指令的单元测试中,我不想担心低级指令中发生了什么.我只想模拟低级指令,基本上它没有做任何事情,所以我可以单独测试顶级指令.
我尝试通过这样的方式覆盖指令定义:
angular.module("myModule").directive("myLowerLevelDirective", function() {
return {
link: function(scope, element, attrs) {
//do nothing
}
}
});
Run Code Online (Sandbox Code Playgroud)
但是,这不会覆盖它,它只是在真正的指令之外运行它.如何阻止这些低级指令在顶级指令的单元测试中做任何事情?
小智 81
指令只是工厂,因此最好的方法是在使用module函数时模拟指令的工厂,通常在beforeEach块中.假设你有一个名为do-something的指令,这个指令被一个名为do-something-else的指令使用,你可以这样模拟它:
beforeEach(module('yourapp/test', function($provide){
$provide.factory('doSomethingDirective', function(){ return {}; });
}));
// Or using the shorthand sytax
beforeEach(module('yourapp/test', { doSomethingDirective: {} ));
Run Code Online (Sandbox Code Playgroud)
然后,在测试中编译模板时,将覆盖该指令
inject(function($compile, $rootScope){
$compile('<do-something-else></do-something-else>', $rootScope.$new());
});
Run Code Online (Sandbox Code Playgroud)
请注意,您需要在名称中添加'Directive'后缀,因为编译器在内部执行此操作:https://github.com/angular/angular.js/blob/821ed310a75719765448e8b15e3a56f0389107a5/src/ng/compile.js#L530
Syl*_*ain 65
模仿指令的干净方法是 $compileProvider
beforeEach(module('plunker', function($compileProvider){
$compileProvider.directive('d1', function(){
var def = {
priority: 100,
terminal: true,
restrict:'EAC',
template:'<div class="mock">this is a mock</div>',
};
return def;
});
}));
Run Code Online (Sandbox Code Playgroud)
您必须确保模拟获得的优先级高于您正在模拟的指令,并且模拟是终端,以便不会编译原始指令.
priority: 100,
terminal: true,
Run Code Online (Sandbox Code Playgroud)
结果如下所示:
鉴于此指令:
var app = angular.module('plunker', []);
app.directive('d1', function(){
var def = {
restrict: 'E',
template:'<div class="d1"> d1 </div>'
}
return def;
});
Run Code Online (Sandbox Code Playgroud)
你可以像这样嘲笑它:
describe('testing with a mock', function() {
var $scope = null;
var el = null;
beforeEach(module('plunker', function($compileProvider){
$compileProvider.directive('d1', function(){
var def = {
priority: 9999,
terminal: true,
restrict:'EAC',
template:'<div class="mock">this is a mock</div>',
};
return def;
});
}));
beforeEach(inject(function($rootScope, $compile) {
$scope = $rootScope.$new();
el = $compile('<div><d1></div>')($scope);
}));
it('should contain mocked element', function() {
expect(el.find('.mock').length).toBe(1);
});
});
Run Code Online (Sandbox Code Playgroud)
还有一些事情:
在创建模拟时,您必须考虑是否需要replace:true和/或a template.例如,如果你模拟ng-src以防止对后端的调用,那么你不想要replace:true而且你不想指定一个template.但如果你嘲笑某些视觉效果,你可能会想要.
如果将优先级设置为100以上,则不会插入mocks的属性.请参阅$ compile源代码.例如,如果你模拟ng-src和设置priority:101,那么你最终ng-src="{{variable}}"不会ng-src="interpolated-value"在你的模拟上.
这是一个有所有东西的plunker.感谢@trodrigues指出我正确的方向.
这是一些解释更多的文档,请查看"配置块"部分.感谢@ebelanger!
Bas*_*dan 29
由于指令注册的实现,似乎不可能用模拟的指令替换现有的指令.
但是,您可以使用多种方法对较高级别的指令进行单元测试,而不会受到较低级别指令的干扰:
1)不要在单元测试模板中使用低级指令:
如果您的更高级别指令未添加您的低级指令,则在您的单元测试中使用仅包含高级指令的模板:
var html = "<div my-higher-level-directive></div>";
$compile(html)(scope);
Run Code Online (Sandbox Code Playgroud)
因此,较低级别的指令不会干扰.
2)在指令实现中使用服务:
您可以通过服务提供较低级别的指令链接功能:
angular.module("myModule").directive("myLowerLevelDirective", function(myService) {
return {
link: myService.lowerLevelDirectiveLinkingFunction
}
});
Run Code Online (Sandbox Code Playgroud)
然后,您可以在单元测试中模拟此服务,以避免干扰您的更高级别指令.如果需要,该服务甚至可以提供整个指令对象.
3)您可以使用终端指令覆盖您的低级指令:
angular.module("myModule").directive("myLowerLevelDirective", function(myService) {
return {
priority: 100000,
terminal: true,
link: function() {
// do nothing
}
}
});
Run Code Online (Sandbox Code Playgroud)
使用终端选项和更高的优先级,将不会执行真正的较低级别指令.指令文档中有更多信息.
了解它在Plunker中的工作原理.
您可以在里面修改模板$templateCache以删除任何较低级别的指令:
beforeEach(angular.mock.inject(function ($templateCache) {
$templateCache.put('path/to/template.html', '<div></div>');
}));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
21419 次 |
| 最近记录: |