4

我如何在这里模拟超时调用?

$scope.submitRequest = function () {

    var formData = getData();

    $scope.form = JSON.parse(formData);

    $timeout(function () {
        $('#submitForm').click();            
    }, 2000);

};

我想看看 timeout 已经用正确的函数调用了。

我想要一个模拟 $timeout 的间谍函数示例。

spyOn(someObject,'$timeout')
4

6 回答 6

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();
于 2018-03-26T21:22:59.190 回答
4

$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 进行测试(这是在 AngularJS 应用程序中尽可能避免使用 jQuery 的众多原因之一)。可以像这个答案中所示的那样监视/模拟 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 });

请注意,预计模拟函数将返回用于与click方法链接的 jQuery 对象。

$(...)被模拟时,测试不需要#submitForm在 DOM 中创建夹具,这是隔离单元测试的首选方式。

于 2018-04-01T20:35:50.627 回答
1

为 $timeout 提供者创建模拟:

var f = () => {} 
var myTimeoutProviderMock = () => f;

用它:

beforeEach(angular.mock.module('myModule', ($provide) => {
  $provide.factory('$timeout', myTimeoutProviderMock);
}))

现在您可以测试:

spyOn(f);
expect(f).toHaveBeenCalled();

PS你最好在超时时测试函数的结果。

于 2018-03-28T14:33:53.603 回答
1

假设那段代码在控制器内或由 $controller 在测试中创建,那么 $timeout 可以在构造参数中传递。所以你可以这样做:

var timeoutStub = sinon.stub();
var myController = $controller('controllerName', timeoutStub);
$scope.submitRequest();
expect(timeoutStub).to.have.been.called;
于 2018-03-29T23:38:53.230 回答
1

带有刷新延迟的单元测试 $timeout

您必须通过调用 $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

于 2018-03-30T05:23:48.157 回答
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

于 2018-03-31T18:22:01.040 回答