angularjs+jasmine:在服务中测试焦点



第一次学习jasmine,在尝试测试角度服务中的focus()功能时遇到了这个错误。这是服务:

myApp.service('MyService', function($timeout, $window) {
var service = {
focusElem: focusElem
};
return service;
function focusElem(id) {
console.log('id of element is = ', id);
if (id) {
$timeout(function() {
var element = $window.document.getElementById(id);
console.log('element is = ', element);
if (element) {
element.focus();
}
});
}
};
});

这是我的规范文件

describe('myApp', function() {
var element, dummyElement;
beforeEach(function() {
// Initialize myApp injector
module('myApp');
// Inject instance of service under test    
inject(function($injector) {
MyServiceObj = $injector.get('MyService');
});
element = angular.element('<input id="firstName" name="firstName"/>');
dummyElement = document.createElement('input');
dummyElement.setAttribute('id', 'lastName');
});
it('should have focus if the focus Service is used on an element', function() {
console.info('------------------');
spyOn(element[0], 'focus');
spyOn(dummyElement, 'focus');
MyServiceObj.focusElem(dummyElement.getAttribute('id'));
expect(dummyElement.focus).toHaveBeenCalled();
});
});

我的错误:

myApp should have focus if the focus Service is used on an element
Expected spy focus to have been called.
Error: Expected spy focus to have been called.

如果您使用ngMock,许多服务都会发生更改,因此可以在测试代码中以同步方式控制它们,从而使您能够更好地控制流。

受影响的服务之一是$timeout

传递给服务内$timeout的函数将不会在测试中执行,除非你告诉它。

要告诉它执行,请像这样使用$timeout.flush()

spyOn(element[0], 'focus');
spyOn(dummyElement, 'focus');
MyServiceObj.focusElem(dummyElement.getAttribute('id'));
$timeout.flush();
expect(dummyElement.focus).toHaveBeenCalled();

请注意,您需要参考$timeout服务:

var element, dummyElement, $timeout;
beforeEach(function() {
module('myApp');
inject(function($injector, _$timeout_) {
MyServiceObj = $injector.get('MyService');
$timeout = _$timeout_;
});

下一个问题是由于您的服务中的以下线路:

var element = $window.document.getElementById(id);

您在测试中创建的元素从未附加到DOM,因此服务将找不到它们。

最简单的解决方案是将元素附加到DOM。在这种情况下,重要的是在测试后手动删除它们,因为Jasmine对整个测试套件使用相同的DOM。

例如:

it('should have focus if the focus Service is used on an element', function() {
var body = angular.element(document.body);
body.append(element);
body.append(dummyElement);
spyOn(element[0], 'focus');
spyOn(dummyElement, 'focus');
MyServiceObj.focusElem(dummyElement.getAttribute('id'));
$timeout.flush();
expect(dummyElement.focus).toHaveBeenCalled();
element.remove();
dummyElement.remove();
});

演示:http://plnkr.co/edit/F8xqfYYQGa15rwuPPbN2?p=preview

现在,在单元测试期间向DOM附加和删除元素并不总是一件好事,而且可能会变得一团糟。

还有其他方法可以处理它,例如监视getElementById并控制返回值,或者模拟整个文档。然而,我不会在这里详述,因为我相信这里已经有这样的例子了。

最新更新