Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS + Karma: reuse a mock service when unit testing directives or controllers

I'm working with AngularJS + Karma. configService manages the settings of my app (e.g. the background-color, wether it's on debug mode, general permissions...). It loads initial data with $http. I wrote the test successfully for the service but my directives and controllers use it.

When I write the unit tests for directives, I have to mock the service.

I know I can do:

spyOn(configService, 'getBackgroundColor').andCallFake(function (params) {
   return "red";
});

but the service has 25+ methods and initial data load. I don't feel like writing (and maintaining) this spyOn thing in every test suite. What's more, I load data in the factory with $http, and that should be mocked as well. if I just inject the service and mock the calls, I'll still make the http get request.

What do you think would be the best way to reuse a mock?

like image 959
Eduard Gamonal Avatar asked May 15 '13 11:05

Eduard Gamonal


People also ask

What is karma in AngularJS?

Karma is a direct product of the AngularJS team from struggling to test their own framework features with existing tools. As a result of this, they made Karma and have transitioned it to Angular as the default test runner for applications created with the Angular CLI.

What is mocking in unit testing Angular?

A mock component in Angular tests can be created by MockComponent function. The mock component respects the interface of its original component, but all its methods are dummies. To create a mock component, simply pass its class into MockComponent function.

Which of the following can be used to run unit tests in Angular?

Jasmine is the default test framework used with Angular.

Which one is the test entry file for the unit test?

The angular-cli configuration of karma uses the file “test. ts” as the entry point of the tests for the application.


1 Answers

Instead of spraying the testing code with spies you can create a proper mock of the service in its own module and add it in any test that needs it.

The controller unit test sits in a test/spec/modules/user/controller.js file.

The mocked service sits in a test/mock/modules/user/service.js file.

For a controller method:

$scope.refreshList = function() {
  UserService.all(pageNumber, size, sort, function(data) {
    $scope.users = data.content;
    $scope.page = data.page;
  });
};

the mocked service:

(function () {

'use strict';

angular.module('app.user.mock', ['app.user']);

angular.module('app.user.mock').factory('UserServiceMock',
  ['$q',
  function($q) {
    var factory = {};

    factory.mockedUsers = { 
      content: [ { firstname: 'Spirou', lastname: 'Fantasio', email: '[email protected]', workPhone: '983743464365' } ],
      page: '1'
    };

    factory.search = function(searchTerm, page, size, sort, callback) {
      var defer = $q.defer();
      defer.resolve(this.mockedUsers);
      defer.promise.then(callback);
      return defer.promise;
    };

    factory.all = function(page, size, sort, callback) {
      var defer = $q.defer();
      defer.resolve(this.mockedUsers);
      defer.promise.then(callback);
      return defer.promise;
    };

    return factory;
  }
]);

})();

and the controller unit test:

(function () {

'use strict';

var $scope;
var listController;
var UserServiceMock;

beforeEach(function() {
  module('app.project');
  module('app.user.mock'); // (1)
});

beforeEach(inject(function($rootScope, _UserServiceMock_) {
  $scope = $rootScope.$new();
  UserServiceMock = _UserServiceMock_; // (2)
}));

describe('user.listCtrl', function() {

  beforeEach(inject(function($controller) {
    listController = $controller('user.listCtrl', {
      $scope: $scope,
      UserService: UserServiceMock
    });
  }));

  it('should have a search function', function () { // (3)
    expect(angular.isFunction(UserServiceMock.search)).toBe(true);
  });

  it('should have an all function', function () {
    expect(angular.isFunction(UserServiceMock.all)).toBe(true);
  });

  it('should have mocked users in the service', function () {
    expect(UserServiceMock.mockedUsers).toBeDefined();
  });

  it('should set the list of users in the scope', function (){
    expect($scope.users).not.toEqual(UserServiceMock.mockedUsers);
    $scope.refreshList();
    $scope.$digest();
    expect($scope.users).toEqual(UserServiceMock.mockedUsers.content);
  });

});

})();

You add the app.user.mock module containing the mocked service (1) and inject the mocked service in the controller (2).

You can then test your mocked service has been injected (3).

like image 151
Stephane Avatar answered Nov 15 '22 22:11

Stephane