Как выполнить юнит-тест / макет вызова $timeout?
Как мне здесь высказать время ожидания?
$scope.submitRequest = function () {
var formData = getData();
$scope.form = JSON.parse(formData);
$timeout(function () {
$('#submitForm').click();
}, 2000);
};
Я хочу видеть, что тайм-аут был вызван с правильной функцией.
Я хотел бы привести пример использования функции spyon для проверки $timeout.
spyOn(someObject,'$timeout')
6 ответов
Прежде всего, манипуляции с 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();
$timeout
можно шпионить или издеваться, как показано в этом ответе:
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 (это одна из многих причин, по которой следует избегать jQuery в приложениях AngularJS, когда это возможно). Можно шпионить / смоделировать jQuery API, как показано в этом ответе.
$(...)
Звонок может быть отслежен с:
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 });
Обратите внимание, что ожидается, что mocked функция вернет объект jQuery для объединения с click
метод.
когда $(...)
издевается, тест не требует #submitForm
Приспособление, которое будет создано в DOM, это предпочтительный способ для изолированного модульного тестирования.
Unit Tesitng $timeout с задержкой сброса
Вы должны очистить очередь службы $ timeout, вызвав $timeout.flush()
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
Создать макет для $timeout провайдера:
var f = () => {}
var myTimeoutProviderMock = () => f;
Используй это:
beforeEach(angular.mock.module('myModule', ($provide) => {
$provide.factory('$timeout', myTimeoutProviderMock);
}))
Теперь вы можете проверить:
spyOn(f);
expect(f).toHaveBeenCalled();
PS вам лучше проверить результат функции в тайм-ауте.
Я полностью согласен с ответом 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();
});
Вот плункер, имеющий оба подхода: http://plnkr.co/edit/s5ls11
Предполагая, что фрагмент кода находится в контроллере или создается в тесте $controller, тогда в параметре конструкции можно передать $timeout. Так что вы можете просто сделать что-то вроде:
var timeoutStub = sinon.stub();
var myController = $controller('controllerName', timeoutStub);
$scope.submitRequest();
expect(timeoutStub).to.have.been.called;