Richard V
Richard V

Reputation: 73

Unit-testing a controller that uses $http

I have a simple controller and the first thing I need it to do is assign a value to scope.

function TestCtrl($scope, $http) {
    $scope.listForms = 'some list';
}

The following test for the controller works as expected:

describe('Testing a controller', function() {

    var ctrl, scope, httpMock;

    beforeEach(inject(function($injector) {
        scope = $injector.get('$rootScope').$new();
        ctrl = $injector.get('$controller');
        ctrl(TestCtrl, { $scope: scope });
    }));

    it("assigns to scope", function() {
      expect(scope.listForms).toMatch("some list");
    });
});

But when I change the function to get the list from my API

function TestCtrl($scope, $http) {
  $http.get('/api/listForms').success(function(list) {
    $scope.aListOfForms = 'some list';
  });
}

and the test changes to

describe('Testing a controller', function() {

    var ctrl, scope, httpMock;

    beforeEach(inject(function($injector) {
        httpMock = $injector.get('$httpBackend');

        scope = $injector.get('$rootScope').$new();
        httpMock.when('GET', '/tactical/api/listOrderForms').respond("an order form");

        ctrl = $injector.get('$controller');
        ctrl(TestCtrl, {
            $scope: scope,
            $http: httpMock
        });
    }));

    it("gets the list from the api and assigns it to scope", function() {
      httpMock.expectGET('tactical/api/listOrderForms');
      expect(scope.orderFormList).toMatch("an order form");
      httpMock.flush();
    });
});

I get the following errors:

TypeError: 'undefined' is not a function
Expected undefined to match 'an order form'.
Error: No pending request to flush !

Does anyone know what I am doing wrong? Thanks in advance.

Upvotes: 7

Views: 8010

Answers (3)

Kenneth Lynne
Kenneth Lynne

Reputation: 15579

$http uses $httpBackend to talk to external resources. You have mocked $httpBackend, but the controller still needs to talk to it trough $https interface.

This should do it:

describe('Testing a controller', function() {

    var ctrl, scope, httpMock;

    beforeEach(inject(function($controller, $rootScope, $httpBackend) {
        httpMock = $httpBackend;

        scope = $rootScope.$new();
        httpMock.when('GET', '/tactical/api/listOrderForms').respond("an order form");

        ctrl = $controller;
        ctrl(TestCtrl, {
            $scope: scope
        });
    }));

    it("gets the list from the api and assigns it to scope", function() {
      httpMock.expectGET('tactical/api/listOrderForms');
      httpMock.flush();
      expect(scope.orderFormList).toMatch("an order form");
    });
});

Upvotes: 8

Paul Lan
Paul Lan

Reputation: 675

you can't replace $http service as $httpBackend service for your controller manually.

Change

    ctrl(TestCtrl, {
        $scope: scope,
        $http: httpMock
    });

to

    ctrl(TestCtrl, {
        $scope: scope
    });

It should work.

Upvotes: 3

Jollymorphic
Jollymorphic

Reputation: 3530

You need to call httpMock.flush() before the expect(). The flush call simulates the response returning from the "back end," calling the success function that was bound to the http request.

Upvotes: 0

Related Questions