Unit testing AngularJS routes throwing Error: Unexpected request - unit-testing

Below is my Jasmine RoutesSpec.js
describe("Todo Routes", function(){
var route;
var rootScope;
var location;
beforeEach(function(){
module('todoApp');
inject(function($route, $location, $rootScope){
route = $route;
location = $location;
rootScope = $rootScope;
});
});
it("should navigate to todo list", function(){
expect(route.current).toBeUndefined();
location.path('/todos');
rootScope.$digest();
expect(route.current.templateUrl).toBe('app/html/listTodos.html');
});
});
Below is my app.js
var todoModule = angular.module("todoApp", []);
todoModule.config(function($routeProvider){
$routeProvider.when('/todos', {
templateUrl: '../html/listTodos.html',
controller: 'TodoListController'
})
.otherwise({redirectTo: '/todos'});
});
todoModule.controller("TodoListController", function($scope, $log){
$scope.todos = [{title: "My first task", done: false}];
$log.log('In list controller');
});
Executing this spec throws me the below error:
Error: Unexpected request: GET ../html/listTodos.html
No more request expected
at Error ()
at $httpBackend (C:/Learn/Javascript/todo_app/libs/angular-mocks.js:934:9)
at sendReq (C:/Learn/Javascript/todo_app/libs/angular.js:9146:9)
at $http (C:/Learn/Javascript/todo_app/libs/angular.js:8937:17)
at Function.$http.(anonymous function) (C:/Learn/Javascript/todo_app/libs/angular.js:9080:18)
at $q.when.then.then.next.locals (C:/Learn/Javascript/todo_app/libs/angular.js:7440:34)
at wrappedCallback (C:/Learn/Javascript/todo_app/libs/angular.js:6846:59)
at wrappedCallback (C:/Learn/Javascript/todo_app/libs/angular.js:6846:59)
at C:/Learn/Javascript/todo_app/libs/angular.js:6883:26
at Object.Scope.$eval (C:/Learn/Javascript/todo_app/libs/angular.js:8057:28)

This means that there is an AJAX call that goes to fetch the template.
$httpBackend.expectGET('app/html/listTodos.html').respond(200) can be put before calling path():
describe("Todo Routes", function(){
var route;
var rootScope;
var location;
var httpBackend;
beforeEach(function(){
module('todoApp');
inject(function($route, $location, $rootScope, $httpBackend){
route = $route;
location = $location;
rootScope = $rootScope;
httpBackend = $httpBackend;
});
});
it("should navigate to todo list", function(){
httpBackend.expectGET('app/html/listTodos.html').respond(200);//mimicking the AJAX call
expect(route.current).toBeUndefined();
location.path('/todos');
rootScope.$digest();
expect(route.current.templateUrl).toBe('app/html/listTodos.html');
});
});

Related

Expected spy go to have been called with [ 'stateName' ] but it was never called?

This is working fine:
spyOn($state, 'go');
But when I execute below line its not working and giving this error:
'Expected spy go to have been called with [ 'stateName' ] but it was
never called'
expect($state.go).toHaveBeenCalledWith('stateName');
describe('Home Controller Test Suite', function() {
var HomeCtrl,$rootScope, $controller, $scope, $http, $state,factory;
var baseURL = "http://localhost:8000/";
beforeEach(module("ui.router"));
beforeEach(module("rbControllers"));
beforeEach(module("ui.bootstrap"));
beforeEach(module("toggle-switch"));
beforeEach(module("ngDraggable"));
beforeEach(module("angular-loading-bar"));
beforeEach(module("ngAnimate"));
beforeEach(module("kendo.directives"));
beforeEach(module("rbApp"));
beforeEach(inject(function(_$rootScope_ ,_$controller_,_$httpBackend_, _$state_){
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$controller = _$controller_;
httpBackend = _$httpBackend_;
$state = _$state_;
HomeCtrl = $controller('HomeCtrl', {'$scope': $scope, '$state': $state, 'baseURL' : baseURL, 'roobrickUiService': factory});
}));
it("Test: HomeCtrl exists or not", function() {
expect(HomeCtrl).toBeDefined();
});
it("Test:xxx run", function() {
spyOn($state, 'go');
expect($state.go).toHaveBeenCalledWith('stateName');
});
});

multiple get requests one controller: Unexpected request, unit testing - AngularJS

I can find plenty of examples of single http calls from a controller and how to test them,but no examples of multiple testing.
My first test works fine without Product.find(10) in the controller. When I add that line however the first test collapses.
The errors:
Error: Unexpected request: GET 0.0.0.0:3000/api/products
No more request expected
and
Error: Unexpected request: GET 0.0.0.0:3000/api/products
No more request expected
I've tried a number of things: including both in the before each, this gave me an undefined error, i tried using expect instead of when, I tried adding both whens to both tests, and a combination of the above. I'm clearly doing something very wrong but being an angular newbie, it's hard to work out exactly what that might be, especially with the lack of examples.. I am just looking to get my first test to pass with Product.find(10)
Here are my tests:
'use strict';
describe('productsController', function() {
var scope, $httpBackend;
var api_root = '0.0.0.0:3000/api/';
beforeEach(angular.mock.module('sprangularApp'));
beforeEach(angular.mock.inject( function($rootScope, $controller, _$httpBackend_) {
$httpBackend = _$httpBackend_;
//Get mock jsons
jasmine.getJSONFixtures().fixturesPath='base/js/tests/api_mock';
scope = $rootScope.$new();
$controller('productsController', {$scope: scope});
}));
//Start Tests
it('Should be array of all products', function() {
$httpBackend.when('GET', api_root + 'products').respond(
getJSONFixture('products.json')
);
$httpBackend.flush();
expect(scope.products[3].name).toBe('Ruby on Rails Bag');
});
it('Should instantiate a new product object from json data', function() {
$httpBackend.when('GET', api_root + 'products/10').respond(
getJSONFixture('10.json')
);
$httpBackend.flush();
expect(scope.currentProduct.name).toBe('Spree Ringer T-Shirt');
});
});
my controller that I am testing:
// Generated by CoffeeScript 1.6.3
(function() {
var sprangularControllers;
sprangularControllers = angular.module('sprangularControllers', ['sprangularServices']);
sprangularControllers.controller('productsController', [
'$scope', 'Product', function($scope, Product) {
Product.products_with_meta().$promise.then(function(response) {
return $scope.products = response.products;
});
return Product.find(10);
}
]);
}).call(this);
And the factory with the resource requests:
sprangularServices = angular.module('sprangularServices', ['ngResource'])
sprangularServices.factory('Defaults', ->
api_url: "0.0.0.0:3000/api/"
)
sprangularServices.factory('Product', ($resource, Defaults) ->
# $resource(Defaults.api_url + 'products.json')
class Product
constructor: ->
#service = $resource(Defaults.api_url + 'products/:id', {id: '#id'})
this.products_with_meta = ->
service = $resource(Defaults.api_url + 'products')
service.get()
this.find = (id) ->
service = $resource(Defaults.api_url + 'products/:id', {id: id})
service.get()
)
As per michael's suggestion I have edited my test to this, however I am still getting the exact same result:
'use strict';
describe('productsController', function() {
var $rootScope, $httpBackend, createController;
var api_root = '0.0.0.0:3000/api/';
beforeEach(angular.mock.module('sprangularApp'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
//Get mock jsons
jasmine.getJSONFixtures().fixturesPath='base/js/tests/api_mock';
$rootScope = $injector.get('$rootScope');
var $controller = $injector.get('$controller');
createController = function() {
return $controller('productsController', {'$scope' : $rootScope });
};
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
//Start Tests
it('Should be array of all products', function() {
$httpBackend.when('GET', api_root + 'products').respond(
getJSONFixture('products.json')
);
var controller = createController();
$httpBackend.flush();
expect($rootScope.products[3].name).toBe('Ruby on Rails Bag');
});
it('Should instantiate a new product object from json data', function() {
$httpBackend.when('GET', api_root + 'products/10').respond(
getJSONFixture('10.json')
);
var controller = createController();
$httpBackend.flush();
expect($rootScope.currentProduct.name).toBe('Spree Ringer T-Shirt');
});
});
Structuring my test in this way seemed to solve the issue:
'use strict';
describe('productsController', function() {
var $rootScope, $httpBackend, createController;
var api_root = '0.0.0.0:3000/api/';
beforeEach(angular.mock.module('sprangularApp'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
//Get mock jsons
jasmine.getJSONFixtures().fixturesPath='base/js/tests/api_mock';
$rootScope = $injector.get('$rootScope');
var $controller = $injector.get('$controller');
createController = function() {
return $controller('productsController', {'$scope' : $rootScope });
};
$httpBackend.when('GET', api_root + 'products').respond(
getJSONFixture('products.json')
);
$httpBackend.when('GET', api_root + 'products/10').respond(
getJSONFixture('10.json')
);
var controller = createController();
$httpBackend.flush();
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
$httpBackend.resetExpectations();
});
//Start Tests
it('Should be array of all products', function() {
expect($rootScope.products[3].name).toBe('Ruby on Rails Bag');
});
it('Should instantiate a new product object from json data', function() {
expect($rootScope.currentProduct.name).toBe('Spree Ringer T-Shirt');
});
});
I suppose the order of define the response, do the http call, flush and do the test is not right.
define how the http call should respond
$httpBackend.when('GET', api_root + 'products').respond(
getJSONFixture('products.json')
);
do the call from your code
$controller('productsController', {$scope: scope});
flush the httpBackend (e.g. simulate the asynchronous behavior of $http)
$httpBackend.flush();
do the test
expect(scope.products[3].name).toBe('Ruby on Rails Bag');
because your controller did a backend call in his constructor and is instantiated before you define what the response should be, you got the error.
Further information and an exmaple the is very close to your use case: http://docs.angularjs.org/api/ngMock.$httpBackend

in angular js while testing the controller got Unknown provider

I have the following controller:
angular.module('samples.controllers',[])
.controller('MainCtrl', ['$scope', 'Samples', function($scope, Samples){
//Controller code
}
Which dependent on the following service:
angular.module('samples.services', []).
factory('Samples', function($http){
// Service code
}
Tried to test the controller using the following code:
describe('Main Controller', function() {
var service, controller, $httpBackend;
beforeEach(module('samples.controllers'));
beforeEach(module('samples.services'));
beforeEach(inject(function(MainCtrl, Samples, _$httpBackend_) {
}));
it('Should fight evil', function() {
});
});
But got the following error:
Error: Unknown provider: MainCtrlProvider <- MainCtrl.
P.s Tried the following post, didn't seem to help
The correct way to test controllers is to use $controller as such:
ctrl = $controller('MainCtrl', {$scope: scope, Samples: service});
Detailed example:
describe('Main Controller', function() {
var ctrl, scope, service;
beforeEach(module('samples'));
beforeEach(inject(function($controller, $rootScope, Samples) {
scope = $rootScope.$new();
service = Samples;
//Create the controller with the new scope
ctrl = $controller('MainCtrl', {
$scope: scope,
Samples: service
});
}));
it('Should call get samples on initialization', function() {
});
});

How to unit test angularjs controller with $location service

I am trying to create a simple unit test that tests my show function.
I get the following error:
TypeError: Object #<Object> has no method 'show'
It seems like $rootScope isn't the scope of the controller?
Here's my controller:
function OpponentsCtrl($scope, $location) {
$scope.show = function(url) {
$location.path(url);
}
}
OpponentsCtrl.$inject = ['$scope', '$location'];
Here's my controller unit test:
describe('OpponentsCtrl', function() {
beforeEach(module(function($provide) {
$provide.factory('OpponentsCtrl', function($location){
// whatever it does...
});
}));
it('should change location when setting it via show function', inject(function($location, $rootScope, OpponentsCtrl) {
$location.path('/new/path');
$rootScope.$apply();
expect($location.path()).toBe('/new/path');
$rootScope.show('/test');
expect($location.path()).toBe('/test');
}));
});
This is how my test ended up working.
describe('OpponentsCtrl', function() {
var scope, rootScope, ctrl, location;
beforeEach(inject(function($location, $rootScope, $controller) {
location = $location;
rootScope = $rootScope;
scope = $rootScope.$new();
ctrl = $controller(OpponentsCtrl, {$scope: scope});
}));
it('should change location when setting it via show function', function() {
location.path('/new/path');
rootScope.$apply();
expect(location.path()).toBe('/new/path');
// test whatever the service should do...
scope.show('/test');
expect(location.path()).toBe('/test');
});
});
Why don't you simply use a spyOn function?
describe('OpponentsCtrl', function() {
var location;
beforeEach(module(function($provide) {
$provide.factory('OpponentsCtrl', function($location){
location = $location;
});
}));
it('should change location when setting it via show function', inject(function() {
spyOn(location, 'path');
expect(location.path).toHaveBeenCalledWith('/new/path');
}));
});
Hope this helps!
I prefer to mock location and services as then it's a unit (not integration) test:
'use strict';
describe('flightController', function () {
var scope;
var searchService;
var location;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
mockSearchService();
mockLocation();
createController($controller);
}));
it('changes location to month page', function () {
searchService.flightToUrl.and.returnValue('Spain/Ukraine/December/1');
scope.showMonth();
expect(location.url).toHaveBeenCalledWith('search/month/Spain/Ukraine/December/1');
});
function mockSearchService() {
searchService = jasmine.createSpyObj('searchService', ['flightToUrl']);
}
function mockLocation() {
location = jasmine.createSpyObj('location', ['url']);
}
function createController($controller) {
$controller('flightController', {
$scope: scope,
searchService: searchService,
$location: location
});
}
});
Cheers

How do you test your emberjs routes?

After a few months without looking at emberjs, I'm trying to go back into it now, and I'm therefore trying the new router. And I would like to test my routes.
Has anybody tried to write some routing tests with emberjs ?
Let's suppose the very basic following router :
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router, context) {
router.get('applicationController').connectOutlet({name: 'home'});
}
})
})
})
How do you test that loading the root.index route properly loads the HomeView ?
Here is the full test, using Jasmine & Sinon :
code:
describe("Given the Router", function(){
var router = null;
beforeEach(function(){
router = Router.create();
});
afterEach(function(){
router = null;
});
it("Should be defined", function(){
expect(router).toBeDefined();
});
it("Should have an root route", function(){
expect(router.get("root")).toBeDefined();
});
describe("its root route", function(){
var root = null;
beforeEach(function(){
root = router.get("root").create();
});
afterEach(function(){
root = null;
});
it("should have an index route", function(){
expect(root.get("index")).toBeDefined();
});
describe("its index route", function(){
var indexRoute = null;
beforeEach(function(){
indexRoute = root.get("index").create();
});
it ("should have route of /", function(){
expect(indexRoute.get("route")).toEqual("/");
});
it ("should connect the outlets to home", function(){
var fakeRouter = Em.Object.create({applicationController: {connectOutlet: function(){} } });
var connectOutletSpy = sinon.spy(fakeRouter.applicationController, "connectOutlet");
var methodCall = connectOutletSpy.withArgs({name:"home"});
indexRoute.connectOutlets(fakeRouter);
expect(methodCall.calledOnce).toBeTruthy();
});
});
});
});
Hope it helps.
Here is how Ember has already tested the connectOutlet for you:
https://github.com/emberjs/ember.js/blob/master/packages/ember-views/tests/system/controller_test.js
test("connectOutlet instantiates a view, controller, and connects them", function() {
var postController = Ember.Controller.create();
var appController = TestApp.ApplicationController.create({
controllers: { postController: postController },
namespace: { PostView: TestApp.PostView }
});
var view = appController.connectOutlet('post');
ok(view instanceof TestApp.PostView, "the view is an instance of PostView");
equal(view.get('controller'), postController, "the controller is looked up on the parent's controllers hash");
equal(appController.get('view'), view, "the app controller's view is set");
});
Other routing related tests can be found at https://github.com/emberjs/ember.js/tree/master/packages/ember-routing/tests