我正在尝试测试角度服务,它通过$document
jasmine服务对DOM进行一些操作.假设它只是向元素添加了一些指令.
这样的服务看起来像
(function(module) { module.service('myService', [ '$document', function($document) { this.doTheJob = function() { $document.find('body').append(''); }; } ]); })(angular.module('my-app'));
我想像这样测试它
describe('Sample test' function() { var myService; var mockDoc; beforeEach(function() { module('my-app'); // Initialize mock somehow. Below won't work indeed, it just shows the intent mockDoc = angular.element(''); module(function($provide) { $provide.value('$document', mockDoc); }); }); beforeEach(inject(function(_myService_) { myService = _myService_; })); it('should append my-directive to body element', function() { myService.doTheJob(); // Check mock's body to contain target directive expect(mockDoc.find('body').html()).toContain(''); }); });
那么问题是创建这样的模拟的最佳方法是什么?
使用real进行测试document
会给我们在每次测试后清理带来很多麻烦,并且看起来不像是一种方法.
我还尝试在每次测试之前创建一个新的真实文档实例,但结果却出现了不同的失败.
创建一个像下面这样的对象并检查whatever
变量是否有效但看起来非常难看
var whatever = []; var fakeDoc = { find: function(tag) { if (tag == 'body') { return function() { var self = this; this.append = function(content) { whatever.add(content); return self; }; }; } } }
我觉得我在这里缺少一些重要的东西而且做错了.
任何帮助深表感谢.
您可以使用DOMImplementation#createHTMLDocument()
以下命令创建空测试文档:
describe('myService', function() {
var $body;
beforeEach(function() {
var doc;
// Create an empty test document based on the current document.
doc = document.implementation.createHTMLDocument();
// Save a reference to the test document's body, for asserting
// changes to it in our tests.
$body = $(doc.body);
// Load our app module and a custom, anonymous module.
module('myApp', function($provide) {
// Declare that this anonymous module provides a service
// called $document that will supersede the built-in $document
// service, injecting our empty test document instead.
$provide.value('$document', $(doc));
});
// ...
});
// ...
});
因为您要为每个测试创建一个新的空文档,所以不会干扰运行测试的页面,并且在测试之间的服务之后不必显式清理.
$document
在这种情况下,您无需模拟服务.使用它的实际实现更容易:
describe('Sample test', function() { var myService; var $document; beforeEach(function() { module('plunker'); }); beforeEach(inject(function(_myService_, _$document_) { myService = _myService_; $document = _$document_; })); it('should append my-directive to body element', function() { myService.doTheJob(); expect($document.find('body').html()).toContain('<my-directive></my-directive>'); }); });
这里的人物.
如果你真的需要嘲笑它,那么我想你必须按照你的方式去做:
$documentMock = { ... }
但这可能会破坏依赖于$document
服务本身的其他事物(例如,使用这种指令createElement
).
UPDATE
如果您需要在每次测试后将文档恢复到一致状态,则可以按以下方式执行操作:
afterEach(function() { $document.find('body').html(''); // or $document.find('body').empty() // if jQuery is available });
Plunker 在这里(我必须使用另一个容器,否则Jasmine结果将不会被渲染).
正如@AlexanderNyrkov在评论中指出的那样,Jasmine和Karma都在body
标签内部有自己的东西,并且通过清空文档主体来擦除它们似乎不是一个好主意.
更新2
我设法部分模拟了$document
服务,因此您可以使用实际的页面文档并将所有内容恢复到有效状态:
beforeEach(function() { module('plunker'); $document = angular.element(document); // This is exactly what Angular does $document.find('body').append('<content></content>'); var originalFind = $document.find; $document.find = function(selector) { if (selector === 'body') { return originalFind.call($document, 'body').find('content'); } else { return originalFind.call($document, selector); } } module(function($provide) { $provide.value('$document', $document); }); }); afterEach(function() { $document.find('body').html(''); });
这里的人物.
我们的想法是body
用你的SUT可以自由操作的新标签替换标签,并且你的测试可以在每个规范的最后安全地清除.