Функция Spy on scope, выполняемая при инициализации контроллера angular
Я хочу проверить, что следующая функция фактически вызвана инициализацией этого контроллера с использованием жасмина. Похоже, что использование шпиона - это путь, он просто не работает, как я ожидал бы, когда я полагаю, что он был вызван в блок "it". Мне интересно, есть ли особый способ проверить, вызывалось ли что-то, когда оно не вызывалось в пределах функции scope, но только в самом контроллере.
App.controller('aCtrl', [ '$scope', function($scope){
$scope.loadResponses = function(){
//do something
}
$scope.loadResponses();
}]);
//spec file
describe('test spec', function(){
beforeEach(
//rootscope assigned to scope, scope injected into controller, controller instantiation.. the expected stuff
spyOn(scope, 'loadResponses');
);
it('should ensure that scope.loadResponses was called upon instantiation of the controller', function(){
expect(scope.loadResponses).toHaveBeenCalled();
});
});
Ответы
Ответ 1
Установка шпиона перед созданием экземпляра контроллера (в beforeEach) - это способ проверки функций контроллера, выполняемых после создания экземпляра.
РЕДАКТИРОВАТЬ: Это еще не все. Как отмечается в комментарии, функция не существует во время создания ctrl. Чтобы шпионить за этим вызовом, вам нужно назначить произвольную функцию переменной (в этом случае вы назначаете scope.getResponses в пустую функцию) в вашем блоке настройки ПОСЛЕ того, у вас есть область действия, но ПЕРЕД тем, как вы создаете экземпляр контроллера. Затем вам нужно написать шпиона (снова в вашем блоке настройки и BEFORE ctrl), и, наконец, вы можете создать экземпляр контроллера и ожидать, что вызов будет выполнен для этой функции. Извините за дрянной ответ изначально
Ответ 2
Вам необходимо инициализировать контроллер самостоятельно с помощью области, которую вы создали. Проблема в том, что вам нужно перестроить свой код. Вы не можете заглядывать в несуществующую функцию, но вам нужно вызвать spyOn перед вызовом функции.
$scope.loadResponses = function(){
//do something
}
// <-- You would need your spy attached here
$scope.loadResponses();
Поскольку вы не можете этого сделать, вам нужно сделать вызов $scope.loadResponses()
в другом месте.
Код, который успешно отслеживает функцию с областью действия, таков:
var scope;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
$controller('aCtrl', {$scope: scope});
scope.$digest();
}));
it("should have been called", function() {
spyOn(scope, "loadResponses");
scope.doTheStuffThatMakedLoadResponsesCalled();
expect(scope.loadResponses).toHaveBeenCalled();
});
Ответ 3
Единственный способ, который я нашел для тестирования этого типа сценариев, - это перемещение метода, который будет протестирован на отдельную зависимость, затем ввести его в контроллер и вместо этого предоставить подделку в тестах.
Вот очень простой рабочий пример:
angular.module('test', [])
.factory('loadResponses', function() {
return function() {
//do something
}
})
.controller('aCtrl', ['$scope', 'loadResponses', function($scope, loadResponses) {
$scope.loadResponses = loadResponses;
$scope.loadResponses();
}]);
describe('test spec', function(){
var scope;
var loadResponsesInvoked = false;
var fakeLoadResponses = function () {
loadResponsesInvoked = true;
}
beforeEach(function () {
module('test', function($provide) {
$provide.value('loadResponses', fakeLoadResponses)
});
inject(function($controller, $rootScope) {
scope = $rootScope.$new();
$controller('aCtrl', { $scope: scope });
});
});
it('should ensure that scope.loadResponses was called upon instantiation of the controller', function () {
expect(loadResponsesInvoked).toBeTruthy();
});
});
Для кода реального мира вам, вероятно, потребуется дополнительная работа (например, вы не всегда можете подделать метод loadResponses
), но вы получите эту идею.
Кроме того, вот хорошая статья, в которой объясняется, как создавать поддельные зависимости, которые на самом деле используют шпионов Jasmine: Mocking Dependencies in AngularJS Tests
EDIT: Вот альтернативный способ, который использует $provide.delegate
и не заменяет оригинальный метод:
describe('test spec', function(){
var scope, loadResponses;
var loadResponsesInvoked = false;
beforeEach(function () {
var loadResponsesDecorator = function ($delegate) {
loadResponsesInvoked = true;
return $delegate;
}
module('test', function($provide) {
$provide.decorator('loadResponses', loadResponsesDecorator);
});
inject(function($controller, $rootScope) {
scope = $rootScope.$new();
$controller('aCtrl', { $scope: scope });
});
});
it('should ensure that scope.loadResponses was called upon instantiation of the controller', function () {
expect(loadResponsesInvoked).toBeTruthy();
});
});
Ответ 4
Я не совсем понял ответы выше.
метод, который я часто использую - не тестируйте, вместо этого проверяйте вывод, который он делает.
вы не указали, что на самом деле делает loadResponses
, но позволяет сказать, что он помещает что-то в область видимости - так что проверьте существование этого.
Кстати - я сам задал аналогичный вопрос, но в изолированном виде
angular - как проверить директиву с помощью изолированной загрузкиScope?
если вы все еще хотите шпионить - в unisolated scope, вы определенно можете использовать технику.
например, измените свой код на
if ( !$scope.loadResponses ){
$scope.loadResponses = function(){}
}
$scope.loadResponses();
Таким образом вы сможете определить шпиона перед инициализацией контроллера.
Другой способ, как и PSL, предлагается в комментариях - переместите loadResponses
в службу, загляните в нее и проверьте, что она была вызвана.
Однако, как уже упоминалось, это не будет работать в изолированной области видимости. И поэтому метод тестирования выходных данных является единственным, который я действительно рекомендую, так как он отвечает на оба сценария.