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

Тестирование $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 работает очень хорошо.

4b9b3361

Ответ 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');