npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

karma-jasmine-angularjs

v1.1.3

Published

Karma jasmine framework for easier angularjs unit tests development

Downloads

824

Readme

karma-jasmine-angularjs

Simpler way to write unit tests for AngularJS with karma and jasmine

Installation

npm install karma-jasmine-angularjs --save-dev

Configuration

karma.conf.js

module.exports = function(config) {
  config.set({
    frameworks: ['jasmine-angularjs'],
    // includes all specs and js files
    files: [
      '*.js'
    ]
  })
}

Setup

Setting up an angularjs Unit Tests might seem complicated, but once you break it down into parts it becomes very understandable. There is a few thing that we need to take into account to setup a proper environment for unit testing angularjs components which are:

The module our component belongs to, our component name, and its dependencies.

Service.

my-service.js

angular
  .module('myModule', [])
  .service('myService', myService);
 
myService () {
  this.sum = function (a, b) {
    return a + b;
  };
}

my-service.spec.js

describe('My service tests', function () {
  var testBed;
  var myService;
  beforeEach(function () {
    testBed = TestBed.configure({
      module: 'myModule',
      service: 'myService'
    });
    
    /*
     * The test bed programagically handles mocking the module, injecting the service and returning an 
     * instance of the given component.
     */
    myService = testBed.service;
  });
  
  describe('initialize'. function () {
    it('Should be defined', function () {
      expect(myService).toBeDefined();
    });
  });
  
  describe('sum', function () {
    it('Should sum two values', function () {
      var valueOne = 2;
      var valueTwo = 2;
      var result = myService.sum(valueOne, valueTwo);
      
      expect(result).toBe(4);
      
    });
  });
});

Factory

my-factory.spec.js

testBed = TestBed.configure({
  module: 'myModule',
  factory: 'myFactory'
});

/*
  * The test bed programagically handles mocking the module, injecting the factory and returning an 
  * instance of the given component.
  */
myFactory = testBed.factory;

Controller

my-controller.spec.js

testBed = TestBed.configure({
  module: 'myModule',
  controller: 'myController'
});

/*
  * The test bed programagically handles mocking the module, injecting the controller and returning an 
  * instance of the given component.
  */
myController = testBed.controller;
$scope = testBed.$scope;

For controllers we make its scope accessible trough the testBed.$scope

Directive

my-directive.spec.js

testBed = TestBed.configure({
  module: 'myModule',
  directive: 'myDirective'
});

myDirective = testBed.directive;
$scope = testBed.$scope;

Directive extras

scope

If our directive has scope properties, we can also mock them by passing data trough the 'scope' property in the configuration object.

require

If our directive uses the require property we can mock a parent by passing its name throughout the 'parent' property

child directives

If our directive contains other custom directives on in its template, to avoid injecting them we also mock them by passing their names into the 'children' property

NOTE: the directive will be rendered as configured in its definition object, these are helper properties to mock all the necessary information that a directive might need to be able to instantiate itself

my-directive.spec.js

testBed = TestBed.configure({
  module: 'myModule',
  directive: 'myDirective',
  scope: { 
    customData: {}
  },
  parent: 'myParentName',
  children: ['myChildCustomDirective']
});

myDirective = testBed.directive;
$scope = testBed.$scope;

Directive templateUrl's

If our directives use templateUrls we handle them with karma's preprocessor "karma-ng-html2js", this will load them in cache and angularjs will fetch them from there https://github.com/karma-runner/karma-ng-html2js-preprocessor#configuration

Handle dependencies

Dependency handling is also very easy, for an AngularJS component to be properly instantiated, all of its dependencies need to be previously injected. Imagine our service looks like this:

my-service.js

angular
  .module('myModule', [])
  .service('myService', myService);
 
myService ($timeout, myOtherService) {
  this.sum = function (a, b) {
    return a + b;
  };
}

Here we have native angular service and our custom service as dependencies, for the test bed to handle this we need to add the following to our configuration object.

testBed = TestBed.configure({
  module: 'myModule',
  service: 'myService',
  dependencies: {
    provide: ['$timeout'],
    mock: {
      myOtherService: null
    }
  }
});

myService = testBed.service;

The provide object will assume that the dependency already exists in the environment and try to fetch it using angular's $inject service; the mock object will be used to create jasmine spy objects for us to mock away all behavior that needs to be mocked from our dependencies. Both provided and mocked dependencies are accessible through the testBed.get('<dependencyName>') method.

Mocking dependencies behavior

Notice how in the mock property we created an object with a null value. The value is reserved for specific methods that we might need to mock inside a specific test for that dependency, for example:

Lets say we have two services:

var myModule = angular.module('myModule', []);

myModule.service('myService', myService);
myService ($timeout, myOtherService) {
  this.sum = function (a, b) {
    var result a + b;
    var isValid = myOtherService.veryComplexLogic(result);
    return isValid ? result : null;
  };
}

myModule.service('myOtherService', myOtherService);
myOtherService($timeout, crazyDependency, anotherDependency) {
  this.veryComplexLogic = function (value) {
    if (typeof value != 'number') {
      value = parseInt(value);
    }
    else {
      // ... More crazy logic with other dependencies and stuff
    }
  };
}

Imagine we want to test "myService.sum" method, but notice that our "myService.sum" method uses another function from one of its dependencies. In reality, we only care to know if "myService.sum" executes its logic correctly so we shouldn't worry about what "myOtherService.veryComplexLogic" does. Not only that but, trying to inject "myOtherService" would require that we also make sure that all of its dependencies are injected in the environment. So, to make this simpler, we can define specific behavior to our mocked dependencies to make sure we only test the code that we want.

testBed = TestBed.configure({
  module: 'myModule',
  service: 'myService',
  dependencies: {
    provide: ['$timeout'],
    mock: {
      myOtherService: {
        veryComplexLogic: function () { return true; }
      }
    }
  }
});

myService = testBed.service;

With this we can always make sure that whenever "myService" needs to execute "myOtherService.veryComplexLogic" it will always return true and we can avoid all the hazard of injecting all the dependencies.

Mocking dependencies on the fly

But, what if we want to mock behavior for a specific test? We can also assign specific behavior for a single test on the fly by calling var myOtherService = testBed.get('myOtherService'); where "myOtherService" will contain an instance of the jasmine spy object created internally for us to control and alter as we need.

my-service.spec.js

describe('My service tests', function () {
  var testBed;
  var myService;
  beforeEach(function () {
    testBed = TestBed.configure({
      module: 'myModule',
      service: 'myService',
      dependencies: {
        mock: {
          myOtherService: {
            veryComplexLogic: function () { return true; }
          }
        }
      }
    });
    
    /*
     * The test bed programagically handles mocking the module, injecting the service and returning an 
     * instance of the given component.
     */
    myService = testBed.service;
  });
  
  describe('initialize'. function () {
    it('Should be defined', function () {
      expect(myService).toBeDefined();
    });
  });
  
  describe('sum', function () {
    it('Override dependencies behavior', function () {
      var valueOne = 2;
      var valueTwo = 2;
      // Use the testBed get method to get the dependency
      var myOtherService = testBed.get('myOtherService');
      // Change myOtherService jasmine spy behavior
      myOtherService.veryComplexLogic.and.callFake(function () {
        return false;
      });

      var result = myService.sum(valueOne, valueTwo);
      expect(result).toBeNull();
      
    });
  });
});

When we define our dependency we can decide what type of behavior it should have, but we can override that behavior at any given time for any test by directly modifying the jasmine spy object created. For more on jasmine spies see: https://jasmine.github.io/api/edge/Spy

This is not a framework or a strict guide on how to write your unit tests, this library is not invasive to the karma, jasmine or angularjs environment, this library is meant to be a plug-n-play helper for faster and more productive unit testing.

Thank you for reading this far! :)