Angular js unit test макет документа
Я пытаюсь протестировать службу angular, которая делает некоторые манипуляции с DOM через службу $document
с жасмином.
Скажем, он просто добавляет некоторую директиву в элемент <body>
.
Такая услуга может выглядеть как
(function(module) {
module.service('myService', [
'$document',
function($document) {
this.doTheJob = function() {
$document.find('body').append('<my-directive></my directive>');
};
}
]);
})(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('<html><head></head><body></body></html>');
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 body to contain target directive
expect(mockDoc.find('body').html()).toContain('<my-directive></my-directive>');
});
});
Итак, вопрос в том, что было бы лучшим способом создания такого макета?
Тестирование с помощью реального 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;
};
};
}
}
}
Я чувствую, что мне не хватает чего-то важного здесь и делаю что-то очень неправильное.
Любая помощь очень ценится.
Ответы
Ответ 1
Вам не нужно издеваться над сервисом $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>');
});
});
Plunker здесь.
Если вам действительно нужно издеваться над этим, тогда я думаю, вам придется делать это так, как вы делали:
$documentMock = { ... }
Но это может сломать другие вещи, которые полагаются на службу $document
(такая директива, которая использует createElement
, например).
ОБНОВЛЕНИЕ
Если вам нужно восстановить документ обратно в согласованное состояние после каждого теста, вы можете сделать что-то в этом направлении:
afterEach(function() {
$document.find('body').html(''); // or $document.find('body').empty()
// if jQuery is available
});
здесь (я должен был использовать другой контейнер в противном случае результаты Жасмин не будет вынесено).
Как @AlexanderNyrkov отметил в комментариях, как жасмин и карма есть свои собственные вещи внутри тега 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('');
});
Plunker здесь.
Идея состоит в том, чтобы заменить тег body
на новый, который ваш SUT может свободно манипулировать, и ваш тест может безопасно очищаться в конце каждой спецификации.
Ответ 2
Вы можете создать пустой тестовый документ, используя 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 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));
});
// ...
});
// ...
});
Поскольку вы создаете новый пустой документ для каждого теста, вы не будете вмешиваться в страницу, на которой выполняются ваши тесты, и вам не придется явно очищать после вашей службы между тестами.