Search code examples
angularjsunit-testingjasminekarma-jasmineangularjs-service

Should Angular Service Execute In Unit Test


I am trying to integrate Karma and Jasmine in to my project.

I have started off with a very basic test to ensure my controller is defined and a $scope variable equals a string - which pass as expected.

My controller, also calls a service which performed a $http.get, when running my test, without any mention of a service, i get the error:

Error: Unexpected request: GET /my/endpoint/
No more request expected

Controller:

define(['module'], function (module) {
    'use strict';

    var MyController = function ($scope, MyService) {

        $scope.testScope = 'karma is working!';

        MyService.getData().then(function (data) {
            $scope.result = data.hour
        });
    };    

    module.exports = ['$scope', 'MyService', MyController ];
});

Test:

define(['require', 'angular-mocks'], function (require) {
    'use strict';

    var angular = require('angular');

    describe("<- MyController Spec ->", function () {    

        var controller, scope;

        beforeEach(angular.mock.module('myApp'));

        beforeEach(inject(function (_$controller_, _$rootScope_) {
            scope = _$rootScope_.$new();
            controller = _$controller_('MyController', {$scope: scope});  
            scope.$apply();
        }));

        it('should verify that the controller exists ', function() {
            expect(controller).toBeDefined();
        });    

        it('should have testScope scope equaling *karma is working*', function() {
            expect(scope.testScope ).toEqual('karma is working!');
        });
    });
});

Are the above errors expected?


UPDATE from response below:

define(['require', 'angular-mocks'], function (require) {
    'use strict';

    var angular = require('angular');

    describe("<- MyController Spec ->", function () {    

        var controller, scope, $httpBackend, myService;

        beforeEach(angular.mock.module('myApp'));

        beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_, _myService_) {
            scope = _$rootScope_.$new();
            $httpBackend = _$httpBackend_;
            $httpBackend.expectGET("/my/endpoint");
            controller = _$controller_('MyController', {$scope: scope});  
            scope.$apply();
        }));

        it('should verify that the controller exists ', function() {
            expect(controller).toBeDefined();
        });    

        it('should have testScope scope equaling *karma is working*', function() {
            expect(scope.testScope ).toEqual('karma is working!');
        });
    });
});

Solution

  • Using Angular Mocks you will always get an error if there is an unexpected or incorrect http request attempted -- even for templates. In your case there are two ways to handle this for testing:

    use $httpBackend

    $httpBackend was designed for testing http requests without actually hitting the wire. In your test, simply add

    $httpBackend.expectGET("/my/endpoint");
    

    before you initialize the controller.

    Mock the service

    The service itself is making the http request, so you can mock the service instead. Services will be injected automatically as usual, but you can explicitly injection whatever you want:

    controller = _$controller_('MyController', {$scope: scope,
    MyService: {getData: () => ({then: () => {}}) });  
    

    This injects an object that has a getData function which returns an object with .then function. Of course this doesn't come close to implementing what you are trying to do, but it is another way to perform the test.

    Both of the above approaches are valid. It depends on what you are testing and what you are trying to accomplish with the testing.