如何模拟超时通话?
$scope.submitRequest = function () {
var formData = getData();
$scope.form = JSON.parse(formData);
$timeout(function () {
$('#submitForm').click();
}, 2000);
};
我希望看到使用正确的函数调用超时。
我想要一个模拟$ timeout的spyon函数的例子。
spyOn(someObject,'$timeout')
答案 0 :(得分:5)
首先,DOM操作只应在指令中执行。 此外,最好使用angular.element(...),而不是$(...)。 最后,为此,您可以将元素的单击处理程序暴露给作用域,监视它,并检查是否已调用该处理程序:
$timeout.flush(2000);
$timeout.verifyNoPendingTasks();
expect(scope.myClickHandler).toHaveBeenCalled();
编辑:
由于这是一个表单,并且没有ng-click处理程序,您可以使用ng-submit处理程序,或者为表单添加名称并执行:
$timeout.flush(2000);
$timeout.verifyNoPendingTasks();
expect(scope.formName.$submitted).toBeTruthy();
答案 1 :(得分:4)
$timeout
可以进行间谍或嘲笑,如this answer:
beforeEach(module('app', ($provide) => {
$provide.decorator('$timeout', ($delegate) => {
var timeoutSpy = jasmine.createSpy().and.returnValue($delegate);
// methods aren't copied automatically to spy
return angular.extend(timeoutSpy, $delegate);
});
}));
这里测试的内容不多,因为使用匿名函数调用$timeout
。出于可测试性原因,将其公开为范围/控制器方法是有意义的:
$scope.submitFormHandler = function () {
$('#submitForm').click();
};
...
$timeout($scope.submitFormHandler, 2000);
然后可以测试间谍$timeout
:
$timeout.and.stub(); // in case we want to test submitFormHandler separately
scope.submitRequest();
expect($timeout).toHaveBeenCalledWith(scope.submitFormHandler, 2000);
$scope.submitFormHandler
内的逻辑可以在不同的测试中进行测试。
这里的另一个问题是jQuery不能很好地与单元测试一起使用,并且需要针对真实DOM进行测试(这是为什么在可能的情况下应该在AngularJS应用程序中避免使用jQuery的众多原因之一)。可以窥探/模拟jQuery API,如this answer中所示。
$(...)
可以通过以下方式调用:
var init = jQuery.prototype.init.bind(jQuery.prototype);
spyOn(jQuery.prototype, 'init').and.callFake(init);
可以嘲笑:
var clickSpy = jasmine.createSpy('click');
spyOn(jQuery.prototype, 'init').and.returnValue({ click: clickSpy });
请注意,模拟函数将返回jQuery对象以使用click
方法进行链接。
当模拟$(...)
时,测试不需要在DOM中创建#submitForm
fixture,这是隔离单元测试的首选方法。
答案 2 :(得分:1)
为$ timeout provider创建模拟:
var f = () => {}
var myTimeoutProviderMock = () => f;
使用它:
beforeEach(angular.mock.module('myModule', ($provide) => {
$provide.factory('$timeout', myTimeoutProviderMock);
}))
现在你可以测试:
spyOn(f);
expect(f).toHaveBeenCalled();
P.S。你最好在超时时测试功能结果。
答案 3 :(得分:1)
假设这段代码在控制器内或由$ controller在测试中创建,那么$ timeout可以在构造参数中传递。所以你可以这样做:
var timeoutStub = sinon.stub();
var myController = $controller('controllerName', timeoutStub);
$scope.submitRequest();
expect(timeoutStub).to.have.been.called;
答案 4 :(得分:1)
单位测试$超时,具有刷新延迟
您必须通过调用$ timeout.flush()
来刷新$ timeout服务的队列describe('controller: myController', function(){
describe('showAlert', function(){
beforeEach(function(){
// Arrange
vm.alertVisible = false;
// Act
vm.showAlert('test alert message');
});
it('should show the alert', function(){
// Assert
assert.isTrue(vm.alertVisible);
});
it('should hide the alert after 5 seconds', function(){
// Act - flush $timeout queue to fire off deferred function
$timeout.flush();
// Assert
assert.isFalse(vm.alertVisible);
});
})
});
请查看此链接http://jasonwatmore.com/post/2015/03/06/angularjs-unit-testing-code-that-uses-timeout
答案 5 :(得分:1)
我完全同意Frane Poljak的回答。你一定要按照自己的方式行事。第二种方法是通过模拟$ timeout服务,如下所示:
describe('MainController', function() {
var $scope, $timeout;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller, $injector) {
$scope = $rootScope.$new();
$timeout = jasmine.createSpy('$timeout');
$controller('MainController', {
$scope: $scope,
$timeout: $timeout
});
}));
it('should submit request, function() {
$scope.submitRequest();
expect($timeout).toHaveBeenCalled();
});
以下是两种方法的plunker:http://plnkr.co/edit/s5ls11