Тестирование $scope в контроллере AngularJs с зависимостью от $filter
Я просмотрел несколько руководств и основных примеров, но мне сложно записывать модульные тесты для моего контроллера. Я видел экземпляры сценариев создания экземпляров и позволяю angular вводить объект $rootScope
, который, в свою очередь, используется для создания нового объекта scope
для контроллера. Но я не могу понять, почему ctrl.$scope
? undefined:
describe('EmployeeCtrl', function () {
var scope, ctrl, $httpBackend;
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller, $filter) {
$httpBackend = _$httpBackend_;
scope = $rootScope.$new();
ctrl = $controller('EmployeeCtrl', { $scope: scope});
expect(ctrl).not.toBeUndefined();
expect(scope).not.toBeUndefined(); //<-- PASS!
expect(ctrl.$scope).not.toBeUndefined(); //<-- FAIL!
}));
});
В итоге я использовал переменную scope
вместо ctrl.$scope
, но затем в своем первом тесте я не мог понять, как unit test a функциональная переменная внутри моего контроллера:
Контроллер:
function EmployeeCtrl($scope, $http, $filter, Employee) {
var searchMatch = function (haystack, needle) {
return false;
}
}
Разбито unit test:
it('should search ', function () {
expect(ctrl.searchMatch('numbers','one')).toBe(false);
});
Это то, что я получаю
TypeError: Object # не имеет метода 'searchMatch'
Как вы проверяете эту функцию? В качестве обходного пути я переместил свой метод в $scope, чтобы проверить на scope.searchMatch
, но мне было интересно, является ли это единственным способом.
Наконец, в моих тестах появляется $filter
и undefined тоже, как вы его вводите? Я пробовал это, но не работал:
ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter });
Спасибо
Update:
Метод, упомянутый выше, для инъекции $filter работает очень хорошо.
Ответы
Ответ 1
Я понимаю, что в Angularjs вы передаете объект области видимости контроллеру, когда приложение запускается, и контроллер изменяет область действия. Контроллер больше не вызывается.
Что делает контроллер в Angularjs, инициализирует область действия: контроллер работает только один раз.
Если вы это понимаете, вы понимаете, что запрашиваете контроллер для области, подобной этой:
currentScope = myController.scope;
не имеет смысла.
(Кстати, одна из вещей, которые мне не нравятся в Angular, - это имена, которые они выбрали. Если какой-то "контроллер" выполняет инициализацию области, то это не действительно контроллер. из этого в API Angular).
Я думаю, что "правильный" способ тестирования "контроллера" создает новую пустую область с нуля в предложении Jasmine beforeEach и использует "контроллер" для инициализации новой пустой области, например this::
var ctrl, myScope;
beforeEach(inject(function($controller, $rootScope) {
myScope = $rootScope.$new();
ctrl = $controller('myController', {
$scope: myScope
});
}));
И затем тестирование новой созданной области имеет ожидаемые свойства:
it('In the scope, the initial value for a=2', function() {
expect(myScope.a).toBe(2);
});
Другими словами, вы не проверяете контроллер; вы проверяете область действия, которую создал контроллер.
Итак, вы делаете это правильно.
Ответ 2
Вот еще один шаблон, который может быть проще:
var $scope;
beforeEach(module(function ($provide) {
$scope = {
// set anything you want here or nothing
};
$provide.value('$scope', $scope);
}));
var ctrl;
beforeEach(inject(function ($controller) {
ctrl = $controller('MyController');
}));
it('should test something...', function () {
// $scope will have any properties set by
// the controller so you can now do asserts
// on them.
});
Вы можете использовать этот же шаблон для установки значений для $location и $window, а также для других.
Ответ 3
Есть только две возможности:
Добавьте searchMatch
в область (как вы упомянули) или
Возвращает функцию searchMatch
:
function EmployeeCtrl($scope, $http, $filter, Employee) {
return {
searchMatch: function (haystack, needle) {
return false;
}
};
}
Если он действительно должен оставаться закрытым, вам придется проверить функции, которые его используют.
Чтобы получить $filter
, вы можете попробовать следующее:
var $filter = angular.injector(['ng']).get('$filter');