Подтвердить что ты не робот

Функция 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();
    });
});
4b9b3361

Ответ 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 в службу, загляните в нее и проверьте, что она была вызвана.

Однако, как уже упоминалось, это не будет работать в изолированной области видимости. И поэтому метод тестирования выходных данных является единственным, который я действительно рекомендую, так как он отвечает на оба сценария.