Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Module Dependencies in Karma/Jasmine for AngularJS

I'm trying to compose some unit tests in Karma/Jasmine for a particular module in my project, destination-filters.

Module Decleration:

angular.module('destination-filter', ['ngSanitize']);

My tests fail unless I remove ngSanitize as a dependency. To my understanding that is because when the module is instantiated it will try and pull in that dependency but because in my spec.js file I haven't declared that module it is failing.

Spec File:

describe('Destination Filter Controller', function () {

  // Set the variables
  var $controller;
  var mockNgSanitize;

  beforeEach(module('destination-filter'));

  beforeEach(function() {
      module(function($provide) {
          $provide.value('ngSanitize', mockNgSanitize);
      });
  });

  beforeEach(inject(function (_$controller_) {
      $controller = _$controller_('DestinationFilterController');
  }));

  it('should expect the controller to not be null', function() {
      // Check the controller is set
      expect($controller).not.toBeNull();
  });

});

Previously, when mocking out services or functions, the $provide method has proven very useful but I'm not sure my use of it is correct here. I'm assuming $provide used in this way can't mock entire modules but rather services?

To clarify, if I remove the ...['ngSantize'])... from my module deceleration the tests instantiate correctly. The error I am receiving with it left in is Error: [$injector:modulerr] destination-filter

like image 619
Aleski Avatar asked Nov 24 '15 09:11

Aleski


1 Answers

There are three options you could take for using ngSanitize in your tests:

  1. inject the service into your test
  2. stub a method call on ngSanitize
  3. mock the entire ngSanitize service

The option you choose is really dependent on the use of ngSanitize in your working code (not your test code).

Whichever one you go for you need to make the service available in your test, there is no need for $provider (this covers option 1 and there is no need to do any more than this if you just want to make this available to your filter):

beforeEach(module('ngSanitize'));    

beforeEach(inject(function(_ngSanitize_) { // the underscores are needed
    mockNgSanitize = _ngSanitize_;
}));

Also, make sure that all js files are picked up and loaded by karma. You can define this in karma.conf.js by adding them to the files: property.

2. Stub a method on the service

I like stubs and find them very useful when writing tests. Your tests should only test one thing, in your case a filter. Stubs give you more control over your tests and allow you to isolate the thing under test.

Typically filters, controllers, anything call on lots of other things (services or factories like $http or ngSanitize).

Assuming that your filter is using ngSanitize's $sanitize to sanitize some html you could stub out that method to return sanitized html you have defined to test against your expectations:

// in a beforeEach

spyOn(mockNgSanitize, "$sanitize").and.returnValue('<some>sanitized<html>');

mockNgSanitized.$sanitize(yourDirtyHtml);

See the jasmine docs for more info.

You might have to play around with spying on the right service but this should work ok.

3. Mock the entire service

I don't think you want to go with this option because it will drive you insane figuring out what needs mocking plus mocks can create unrealistic expectations and also, not particularly useful for your use case. If you really want to have a go then something like the below is heading in the right direction (again see the jasmine docs)

beforeEach(function() {
    mockNgSanitize = ('ngSanitize', ['linky', '$sanitize', '$sanitizeProvider'];
});

it('mocks the ngSanitize service', function() {
    expect(mockNgSanitize.linky).toBeDefined(); 
});

NB: in all the code above make sure you continue to declare any variables up at the top of your describe block.

like image 66
br3w5 Avatar answered Sep 27 '22 23:09

br3w5