GithubHelp home page GithubHelp logo

angularjs-testing-cheat-sheet's Introduction

AngularJS Testing Cheat Sheet

Contents

Unit testing routes
Unit testing controllers

### Unit testing routes

Let's assume we have this routing setup:

angular.module('myApp', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
	$routeProvider
		.when('/', {
			'templateUrl' : 'templates/main.html',
			'controller' : 'HomeController'
		})
		.when('/login', {
			'templateUrl' : 'templates/login.html',
			'controller' : 'LoginController'
		})
		.otherwise({
			'redirectTo' : '/'
		});
}]);

In order to set up our unit tests so that they can test our routing code, we need to do a few things:

  • Inject the $route, $location and $rootScope services
  • Set up a mock back end to handle XHR fetching template code
  • Set a location and run a $digest lifecycle
describe('Routes test', function() {
	// Mock our module in our tests
	beforeEach(module('myApp'));

	// We want to store a copy of the three services we'll use in our tests
	// so we can later reference these services in our tests.
	var $location, $route, $rootScope;

	// We added _ in our dependencies to avoid conflicting with our variables.
	// Angularjs will strip out the _ and inject the dependencies.
	beforeEach(inject(function(_$location_, _$route_, _$rootScope_){
		$location = _$location_;
		$route = _$route_;
		$rootScope = _$rootScope_;
	}));

	// We need to setup a mock backend to handle the fetching of templates from the 'templateUrl'.
	beforeEach(inject(function($httpBackend){
		$httpBackend.expectGET('templates/main.html').respond(200, 'main HTML');
		// or we can use $templateCache service to store the template.
		// $routeProvider will search for the template in the $templateCache first
		// before fetching it using http
		// $templateCache.put('templates/main.html', 'main HTML');
	}));

	// Our test code is set up. We can now start writing the tests.

	// When a user navigates to the index page, they are shown the index page with the proper
	// controller
	it('should load the index page on successful load of /', function(){
		expect($location.path()).toBe('');

		$location.path('/');

		// The router works with the digest lifecycle, wherein after the location is set,
		// it takes a single digest loop cycle to process the route,
		// transform the page content, and finish the routing.
		// In order to trigger the location request, we’ll run a digest cycle (on the $rootScope) 
		// and check that the controller is as expected.
		$rootScope.$digest();

		expect($location.path()).toBe( '/' );
		expect($route.current.controller).toBe('HomeController');
	});

	it('should redirect to the index path on non-existent route', function(){
		expect($location.path()).toBe('');

		$location.path('/a/non-existent/route');

		$rootScope.$digest();

		expect($location.path()).toBe( '/' );
		expect($route.current.controller).toBe('HomeController');
	});
});

resolves

Resolves are a way of executing and finishing asynchronous tasks before a particular route is loaded. This is a great way to check if user is logged in, has authorizations and permissions, and even pre-load some data before a controller and route are loaded into the view.

Consider this routing configuration where we add a resolve which returns a promise:

angular.module('myApp', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
	$routeProvider
		.when('/', {
			'templateUrl' : 'templates/main.html',
			'controller' : 'HomeController',
			'resolve' : {
				'allowAccess' : ['$http', function($http){
					return $http.get('/api/has_access');
				}]
			}
		}
		})
		.otherwise({
			'redirectTo' : '/'
		});
}]);

In our test, we mock out each resolves dependencies and spy on each method using jasmine.createSpy() if you care if the method has been invoked.

describe('Routes test with resolves', function() {
	var httpMock = {};

	beforeEach(module('myApp', function($provide){
		$provide('$http', httpMock);
	}));

	var $location, $route, $rootScope;

	beforeEach(inject(function(_$location_, _$route_, _$rootScope_, $httpBackend, $templateCache){
		$location = _$location_;
		$route = _$route_;
		$rootScope = _$rootScope_;

		$templateCache.put('templates/main.html', 'main HTML');

		httpMock.get = jasmine.createSpy('spy').and.returnValue('test');
	}));

	it('should load the index page on successful load of /', 
		inject(function($injector){
			expect($location.path()).toBe( '' );
			$location.path('/');

			$rootScope.$digest();

			expect($location.path()).toBe( '/' );
			expect($route.current.controller).toBe('HomeController');

			// We need to do $injector.invoke to resolve dependencies
			expect($injector.invoke($route.current.resolve.allowAccess)).toBe('test');
	}));
});
### Unit testing controllers

The controller is where we handle updating the views in our application. In setting up unit tests for controllers, we need to make sure:

  1. Set up our tests to mock the module.
  2. Store an instance of the controller with an instance of the known scope.
  3. Test our expectations against the scope.

Note: Testing controllerAs syntax is different than controllers that use $scope. Here we're testing controllers that attaches data and method in the $scope object.

Consider this simplistic controller

angular.module('myApp', [])
.controller('ListCtrl', ['$scope', function($scope){
	$scope.items = [
		{'id': 1, 'label': 'First', 'done': true},
		{'id': 2, 'label': 'Second', 'done': false}
	];

	$scope.getDoneClass = function(item) {
		return {
			'finished': item.done,
			'unfinished': !item.done
		};
	}
}]);

All it does is assign an array to its instance (to make it available to the HTML), and then has a function to figure out the presentation logic, which returns the classes to apply based on the item’s done state. Given this controller, let us take a look at how a Jasmine spec for this might look like:

describe('ListCtrl', function(){
	// Mock the module myApp
	beforeEach(module('myApp'));

	var scope;

	beforeEach(inject(function($controller, $rootScope){
		// To instantiate a new controller instance, we need to create 
		// a new instance of a scope from the $rootScope with the $new() method. 
		// This new instance will set up the scope inheritance that Angular uses at run time.
		// With this scope, we can instantiate a new controller and 
		// pass the scope in as the $scope of the controller.
		scope = $rootScope.$new();

		// Create the new instance of the controller
		$controller('ListCtrl', {'$scope' : scope});
	}));

	// We'll test the two features of our controller
	// 1. The items data is populated on load
	// 2. The 'getDoneClass' method returns object based on item argument

	it('should have items available on load', function() {
		expect(scope.items).toEqual([
			{'id': 1, 'label': 'First', 'done': true},
			{'id': 2, 'label': 'Second', 'done': false}
		]);
	});

	it('should have highlight items based on state', function(){
		var item = {'id': 1, 'label': 'First', 'done': true};

		var actualClass = scope.getDoneClass(item);
		expect(actualClass.finished).toBeTruthy();
		expect(actualClass.unfinished).toBeFalsy();

		item.done = false;
		actualClass = scope.getDoneClass(item);
		expect(actualClass.finished).toBeFalsy();
		expect(actualClass.unfinished).toBeTruthy();
	});

});

angularjs-testing-cheat-sheet's People

Contributors

sydcanem avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.