单元测试Angularjs指令,包含使用Jasmine的私有超时 [英] Unit Test Angularjs directive, which contains private timeout, with Jasmine
问题描述
我有一个指令,其行为应该有所不同,具体取决于自初始化以来经过的时间:
I have a directive, which should behave differently, depending on how much time passed since it's initialization:
am.directive('showText', () => ({
restrict: 'E',
replace: true,
scope: {
value: '@'
},
controller: ($scope, $timeout) => {
console.log('timeout triggered');
$scope.textVisible = false;
let visibilityCheckTimeout = $timeout(() => {
if (parseInt($scope.value, 10) < 100) {
$scope.textVisible = true;
}
}, 330);
// Clear timeout upon directive destruction
$scope.$on('$destroy', $timeout.cancel(visibilityCheckTimeout));
},
}));
问题是,当我尝试用Jasmine测试它时,我似乎无法忍受找到一种以任何方式触发此超时的方法。已经尝试了 $ timeout.flush()
和 $ timeout.verifyNoPendingTasks()
(实际上会抛出错误,如果我会评论 flush
call)。但它仍然没有触发超时的回调执行
The problem is, that when I'm trying to test it with Jasmine, I can't seem to find a way to trigger this timeout in any way. Already tried a $timeout.flush()
and $timeout.verifyNoPendingTasks()
(which actually throws an error, if I'll comment flush
call). But it's still not triggering that timeout's callback execution
describe('showText.', () => {
let $compile;
let $rootScope;
let $scope;
let $timeout;
const compileElement = (rootScope, value = 0) => {
$scope = rootScope.$new();
$scope.value = value;
const element = $compile(`
<show-text
value="value"
></show-text>
`)($scope);
$scope.$digest();
return element;
};
beforeEach(() => {
module('app.directives.showText');
inject((_$compile_, _$rootScope_, _$timeout_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$timeout = _$timeout_;
});
});
it(`Process lasts > 0.33s. Should show text.`, () => {
const VALUE = 30;
const element = compileElement($rootScope, VALUE);
const elementContent = element.find('.show-text__content');
$timeout.flush(1000);
$timeout.verifyNoPendingTasks();
$rootScope.$digest();
expect(element.isolateScope().textVisible).toBeTruthy();
expect(elementContent.length).toEqual(1);
expect(elementContent.text().trim()).toBe('Example text');
});
});
测试失败。
不能找到我做错了什么。关于如何正确测试这种情况的任何提示?
Can't find what am I doing wrong. Any tips on how to properly test such a case?
谢谢。
UPD
经过一些调查,我发现在这个特定的测试用例中,在 compileElement
函数中, value
属性未被 $ compile
服务评估。并等于value
。我已经使用了相同的功能10次,并且无法获得,为什么它没有采用之前的 $ scope.value
的属性。
UPD
After some investigation, I've found that in this particular test-case, in compileElement
function, value
property isn't being evaluated by $compile
service. And equals "value"
. I've used same function already 10th of times, and can't get, why it's not taking a $scope.value
's property as it was before.
推荐答案
发生这种情况的原因是 $ timeout.cancel(visibilityCheckTimeout)
启动无条件地立即。相反,它应该是
The reason why this happens is that $timeout.cancel(visibilityCheckTimeout)
launches unconditionally and immediately. Instead, it should be
$scope.$on('$destroy', () => $timeout.cancel(visibilityCheckTimeout));
可以采取一些措施来提高可测试性(除了之外) $ timeout
在这里作为一次性范围观察者工作并要求替换为一个)。
There are things that can be done to improve testability (besides the fact that $timeout
works here as one-time scope watcher and asks to be replaced with the one).
$ timeout
可以成功间谍:
beforeEach(module('app', ($provide) => {
$provide.decorator('$timeout', ($delegate) => {
var timeoutSpy = jasmine.createSpy().and.returnValue($delegate);
angular.extend(timeoutSpy, $delegate);
spyOn(timeoutSpy, 'cancel').and.callThrough();
return timeoutSpy;
});
}));
私有 $ timeout
回调可以暴露给范围。
Private $timeout
callback can be exposed to scope.
$scope._visibilityCheckHandler = () => {
if (parseInt($scope.value, 10) < 100) {
$scope.textVisible = true;
}
};
$timeout($scope._visibilityCheckHandler, 330);
通过这种方式可以窥探所有电话并获得全面保障:
This way all of the calls can be spied and get full coverage:
let directiveScope;
...
const element = $compile(`...`)($scope);
directiveScope = element.isolateScope();
spyOn(directiveScope, '_visibilityCheckHandler').and.callThrough();
$scope.$digest();
...
expect($timeout).toHaveBeenCalledWith(directiveScope._visibilityCheckHandler, 330);
expect($timeout.cancel).not.toHaveBeenCalled();
在这种情况下,这里不需要为'> = 0.33s'和'<提供单独的规格。 0.33s'与 flush
延迟参数, $ timeout
已经在Angular规范中测试了内部工作。此外,回调逻辑可以单独测试 $ timeout
spec。
In this case here's no need to have separate specs for '>= 0.33s' and '< 0.33s' with flush
delay argument, $timeout
's inner work was already tested in Angular specs. Also, callback logic can be tested separately from $timeout
spec.
这篇关于单元测试Angularjs指令,包含使用Jasmine的私有超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!