Testing $scope in AngularJs controller with dependency on $filter

I've gone through a few tutorials and basic examples but I'm having a hard time writing unit tests for my controller. I've seen code snippets instantiating controllers and letting angular inject the $rootScope object which in turn is used to create a new scope object for the controller. But I can't figure out why ctrl.$scope? is 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!       
    }));
});

I ended up using the scope variable instead of ctrl.$scope but then on my first test I couldn't figure out how to unit test a function variable inside my controller:

Controller:

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

Broken unit test:

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

This is what I get

TypeError: Object # has no method 'searchMatch'

How do you test that function? As a workaround I moved my method to $scope so I could test for scope.searchMatch but I was wondering if this is the only way.

Finally, on my tests is appears $filter is undefined too, how do you inject it? I tried this but didn't work:

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

Thanks

Update: The method mentioned above to inject $filter works just fine.

My understanding is that, in Angularjs, you pass a scope object to the controller when the application is starting and the controller modify the scope. The controller is not called anymore.

What a controller is really doing, in Angularjs, is initializing the scope: the controller only runs one time. If you understand this, you realize that asking to the controller for the scope like this:

currentScope = myController.scope;

doesn't makes sense.

(Incidentally, one of the things I don't like in Angular is the names that they have choosen. If what a 'controller' is doing is initializing the scope then it's not really a controller. There are a lot of this in the Angular API).

I think that the 'proper' way for testing a 'controller' is creating a new blank scope from scratch in a Jasmine beforeEach clause and using the 'controller' for initializing a new blank scope like this::

var ctrl, myScope;

beforeEach(inject(function($controller, $rootScope) {
    myScope = $rootScope.$new();
    ctrl = $controller('myController', {
        $scope: myScope
    });
}));

And then testing the new created scope has the expected properties:

it('In the scope, the initial value for a=2', function() {
            expect(myScope.a).toBe(2);
});

In other words, you don’t test the controller; you test the scope that the controller has created.

So, you're doing it right.

There are only two possibilities:

Add searchMatch to the scope (as you mentioned) or

Return the searchMatch function:

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

If it really must stay private, then you'll have to test the features which use it.

To get the $filter, you can try the following:

var $filter = angular.injector(['ng']).get('$filter'); 

Here is another pattern that might be simpler to follow:

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

You can use this same pattern to set values for $location and $window as well as others.