Тестирование $scope в контроллере AngularJs с зависимостью от $filter

Я прошел через несколько руководств и базовых примеров, но мне трудно писать модульные тесты для моего контроллера. Я видел фрагменты кода, создающие экземпляры контроллеров и позволяющие angular вводить объект $rootScope, который, в свою очередь, используется для создания нового объекта scope для контроллера. Но я не могу понять, почему ctrl.$scope? не определено:

 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, но затем в своем первом тесте я не мог понять, как выполнить модульное тестирование переменной функции внутри моего контроллера:

Контроллер:

 function EmployeeCtrl($scope, $http, $filter, Employee) {
  var searchMatch = function (haystack, needle) {
   return false;
  }
 }

Неработающий модульный тест:

it('should search ', function () {                
    expect(ctrl.searchMatch('numbers','one')).toBe(false);
});

Это то, что я получаю

TypeError: объект # не имеет метода 'searchMatch'

Как вы проверяете эту функцию? В качестве обходного пути я переместил свой метод в $scope, чтобы проверить scope.searchMatch, но мне было интересно, единственный ли это способ.

Наконец, в моих тестах появляется $filter тоже не определено, как его внедрить? Я пробовал это, но не работал:

ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter });

Спасибо

Обновление: упомянутый выше метод внедрения $filter отлично работает.


person Ulises    schedule 07.01.2013    source источник
comment
мой тест жасмина прерывается методом инъекции. Это не найдено. Что мне не хватает. угловой не равен нулю. У меня есть тест, который проходит проверку загрузки.   -  person Hcabnettek    schedule 22.03.2013


Ответы (4)


Насколько я понимаю, в Angularjs вы передаете объект области контроллеру при запуске приложения, и контроллер изменяет область. Контроллер больше не вызывается.

Что на самом деле делает контроллер в Angularjs, так это инициализирует область действия: контроллер запускается только один раз. Если вы это понимаете, то понимаете, что запрашивать у контроллера область действия можно так:

currentScope = myController.scope;

не имеет смысла.

(Между прочим, одна из вещей, которые мне не нравятся в Angular, — это имена, которые они выбирают. Если то, что делает «контроллер», — это инициализация области видимости, то на самом деле это не контроллер. В Angular много всего этого. API).

Я думаю, что «правильный» способ тестирования «контроллера» — создать новую пустую область с нуля в предложении Jasmine beforeEach и использовать «контроллер» для инициализации новая пустая область, подобная этой::

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);
});

Другими словами, вы не тестируете контроллер; вы проверяете область, созданную контроллером.

Итак, вы делаете это правильно.

person Robert    schedule 11.01.2013
comment
Это имеет больше смысла. Большое спасибо ! - person Ulises; 13.01.2013

Вот еще один шаблон, которому, возможно, будет проще следовать:

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, а также других.

person Guy    schedule 04.10.2014

Есть только две возможности:

Добавьте searchMatch в область действия (как вы упомянули) или

Вернуть функцию searchMatch:

function EmployeeCtrl($scope, $http, $filter, Employee) {
  return {
    searchMatch: function (haystack, needle) {
      return false;
    }
  };
 }

Если он действительно должен оставаться приватным, вам придется протестировать функции, которые его используют.

Чтобы получить $filter, вы можете попробовать следующее:

var $filter = angular.injector(['ng']).get('$filter'); 
person asgoth    schedule 07.01.2013
comment
Спасибо, а как мне ввести $filter? - person Ulises; 08.01.2013
comment
Я обновил свой ответ. Попробуйте использовать angular.injector(), который возвращает объект $injector, содержащий службы angular, такие как $filter, $controller,... - person asgoth; 08.01.2013

Чтобы протестировать фильтр, выполните следующие действия, чтобы получить целевой фильтр:

var ctrl, myScope, $filter;
beforeEach(inject(function(_$filter_){
    $filter = _$filter_;
}))

Если имя вашего фильтра — AccountTimeZone, сделайте следующее:

let accountTimeZoneFilter = $filter('AccountTimeZone');
person MING WU    schedule 24.05.2021