I have an angular app, that on initialisation, make a number of http requests.
I have set up a test, to expect the first request, and the second,
describe("MyController--", function () {
var controller, scope, $httpBackend, myFactory;
var iResponse = {
table: 'red',
price: '1.99'
};
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_, _myFactory_) {
scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
$httpBackend.expectGET("/app/shopping/cart/layout.html").respond({});
$httpBackend.expectGET("/app/rest/products").respond(iResponse);
myFactory = _myFactory_;
spyOn(myFactory, 'getData').and.callThrough();
controller = _$controller_('MainController', {$scope: scope});
scope.$apply();
$httpBackend.flush();
}));
it('should verify that the controller exists ', function() {
expect(controller).toBeDefined();
});
With the above, i keep seeing the error:
Error: Unexpected request: GET /app/rest/products
Information: Expected GET /app/shopping/cart/layout.html
Any ideas what i am missing?
Firstly I would preload HTML in karma conf so u don't have to expect HTML
Secondly does your controller make a call to the unexpected url? If so u shud expect the request
All the best
Related
In my unit test for a service, I'm testing a GET request. I'm getting the following error, even when I explicitly state $httpBackend.expectGET('/api/example').respond(200, {});.
Error: Unexpected request: GET /api/another/example
Expected GET /api/example
Unit test
describe('MyService', function() {
var MyService,
$httpBackend;
beforeEach(module('example'));
beforeEach(function() {
inject(function(_MyService_, _$httpBackend_) {
MyService = _MyService_;
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/api/example').respond(200, {});
});
})
describe('#get', function() {
beforeEach(function() {
// this is what i want to test
$httpBackend
.expectGET('/api/another/example')
.respond(200, {});
});
it('should send a get request', function() {
MyService.get();
$httpBackend.flush();
});
});
});
I can't see the content of your MyService.get() function, but I'll go ahead and assume it's calling GET /api/another/example.
If I remember correctly, you have to serve your expected requests on $httpBackend in the order you list them.
Assuming both beforeEach segments get triggered you would have to set off a GET request against /api/example before you can access GET /api/another/example.
My code of test_Spec.js
describe('Myctrl', function() {
var $httpBackend, scope, createController, authRequestHandler;
// Set up the module
beforeEach(module('myApp'));
beforeEach(inject(function($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
authRequestHandler = $httpBackend.when('GET', 'http://www.w3schools.com/angular/customers.php')
.respond(true);
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
createController = function() {
return $controller('Myctrl', {'$scope' : scope});
};
})
);
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should fetch authentication token', function() {
$httpBackend.expectGET('http://www.w3schools.com/angular/customers.php');
var controller = createController();
expect(scope.names).toBe(true);
$httpBackend.flush();
});
});
This is my test.js service call
var myApp = angular.module('myApp', []);
app.controller('Myctrl', function($Scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function(response) {$Scope.names = response.records;});
});
and these are the errors that I'm getting:
Here is a summary of the errors:
Error: [$injector:unpr] Unknown provider: $rootScopeProvider <- $rootScope <- $httpBackend http://errors.angularjs.org/1.2.0/$injector/unpr?p0=%24rootScopeProvider%20%3C-%20%24rootScope%20%3C-%20%24httpBackend
TypeError: Cannot read property 'expectGET' of undefined
TypeError: Cannot read property 'verifyNoOutstandingExpectation' of undefined
What is the solution for the errors? I have tried several things but I can't figure out the exact solution. Please assist on this.
You have a typo in your dependencies that you want to inject into your controller.
Change $Scope to $scope ;)
As for the error in your test, try injecting the services this way:
beforeEach(inject(function (_$httpBackend_, _$rootScope_, _$controller_) {
…
Well, I know it is too late to answer but in case anyone else stumbles upon similar error, you can try the following:
You never defined the scope variable. You just declared it. So it actually should be:
scope = $injector.get('$rootScope').$new();
Also note the $new(). For every new test case, (as a best practice) you should get a new scope.
I am expending a lot of time trying to understand how the $httpBackend and the angular-translate could work together in order to test if the translation functionality still works.
I am in this point, and I really don't know how to solve this problem.
'use strict';
describe('Directive: translate', function () {
beforeEach(function () {
angular.module('myApp', ['pascalprecht.translate']);
});
var element,
$compile,
$rootScope,
$http,
$httpBackend;
beforeEach(inject(function (_$rootScope_, _$compile_, _$httpBackend_, _$http_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$http = _$http_;
$httpBackend = _$httpBackend_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should translate to English', function () {
element = $compile('<p translate>discover_more</p>')($rootScope);
$rootScope.$digest();
$httpBackend.expect('GET', 'langs/en.json').respond(200); // Should I return some data at this point?
$http.get('langs/en.json').then(function () {}); // Should I do something here?
$httpBackend.flush();
expect(element.html()).toBe('Discover more');
});
});
My test of course fails. The thing is that I don't know how to 1) really get the JSON with the data and 2) say the directive "here is your data, do your work".
Edit:
Ok, some light over the issue. I just was looking at the testing of this angular module (https://github.com/angular-translate/angular-translate/tree/master/test/unit/directive) and I could make it work:
'use strict';
describe('Directive: translate', function () {
beforeEach(function () {
angular.module('gajoApp', ['pascalprecht.translate']);
});
var element,
$compile,
$rootScope;
beforeEach(module('pascalprecht.translate', function ($translateProvider) {
$translateProvider
.translations('en', {
'discover_more': 'Discover more'
})
.preferredLanguage('en');
}));
beforeEach(inject(function (_$rootScope_, _$compile_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('should translate to English', function () {
element = $compile('<p translate>discover_more</p>')($rootScope);
$rootScope.$digest();
expect(element.html()).toBe('Discover more');
});
});
What I would like, however, is combine this solution with the proper AJAX calls that return the JSON, to test that this is been done too.
You should return from your expected GET request whatever the angular-translate need to actually replace the discover_more in your element.
beforeEach(function () {
$httpBackend.when(
'GET',
'langs/en.json'
).respond({'discover_more': 'Discover more'});
});
Note, that I dont know the excact object that angular-translate expects, so it could differ from my suggestion. Anyway return it when the GET request is perceived.
In addition you're not supposed to make the GET request yourself within your test. If everything is set uo correctly, it should work if you add the expected return to your expected GET request.
Unfortunately it is due to restrictions on angular-translate, but you can use your real JSON locale file by either:
1) Using a plugin to load JSON files combined with $httpBackend to load your locale file when angular-translate requests it.
beforeEach(inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('locale-pt.json').respond(readJSON('langs/en.json'));
$httpBackend.flush();
})));
2) Overriding your app's translations with the $translateProvider.translations() method with a JSON read by a plugin to load JSON files
beforeEach(module(function ($translateProvider) {
$translateProvider.translations('en', readJSON('langs/en.json'));
}));
Note this should be below your beforeEach(module('myApp')); or you will get an $injector error.
I put the code in a fiddle so it can be easily updated and 'worked with' if needed.
describe('PlayersListCtrl', function() { // Jasmine Test Suite
beforeEach(module('wc2014App'));
var ctrl, scope, $httpBackend;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
ctrl = $controller('PlayersListCtrl', {
$scope: scope
});
}));
it('should have an empty player array', function() {
expect(scope.players.length).toBe(0);
});
describe('PlayersListCtrl', function() {
var $httpBackend, $rootScope, createController;
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', '../app/stubs/players.json').respond(
{userId: 'userX'},
{'A-Token': 'xxx'});
$rootScope = $injector.get('$rootScope');
var $controller = $injector.get('$controller');
createController = function() {
return $controller('PlayersListCtrl', {'$scope' : $rootScope });
};
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should fetch authentication token', function() {
$httpBackend.expectGET('../app/stubs/players.json');
var controller = createController();
$httpBackend.flush();
});
});
});
The rest, cause its quite verbose, is in the fiddle: http://jsfiddle.net/tLte2/
Basically the first test passes, not a hard one, but the second one depends on a JSON stub and gives errors like:
PhantomJS 1.9.7 (Mac OS X) PlayersListCtrl PlayersListCtrl should fetch authentication token FAILED
Error: No pending request to flush !
Cant seem to get a grip on how this $httpBackend stiff works. Is must be possible to just fire it and set the result in the scope of the controller?
--edit
Basically got everything wired up perfectly and can do some simple tests that run just fine, however getting JSON stub data in there seems to be a pain. Workaround can be just defining the array described in the the JSON on the controller scope like: controller.players = ['one','two','three',..... etc ......]
But that doesnt feel right. That $httpBackend stuff shouldn't be that hard to fix right?
In angular js unit test, I wanted to set xhr response to each test(in "it" method"), not in beforeEach method, but it seems not working.?
This works
describe('ExampleListCtrl', function(){
var $scope, $httpBackend;
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('examples').respond([]); // <--- THIS ONE
$controller(ExampleListCtrl, {$scope: $scope = $rootScope.$new()});
}));
it('should create "examples" with 0 example fetched', function() {
expect($scope.examples).toBeUndefined();
$httpBackend.flush();
expect($scope.examples).toEqual([]);
});
});
result
Executed 8 of 8 SUCCESS (0.366 secs / 0.043 secs)
But this fails with error when I move expectGet method to each method. I don't know why.
describe('ExampleListCtrl', function(){
var $scope, $httpBackend;
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$controller(ExampleListCtrl, {$scope: $scope = $rootScope.$new()});
}));
it('should create "examples" with 0 example fetched', function() {
$httpBackend.expectGET('examples').respond([]); // <--- MOVED TO HERE
expect($scope.examples).toBeUndefined();
$httpBackend.flush();
expect($scope.examples).toEqual([]);
});
});
Here is the error
....
at /Users/me/app/test/unit/controllers/ExampleListCtrlSpec.js:3:1
Error: No pending request to flush
at Error (<anonymous>)
at Function.$httpBackend.flush (/Users/me/app/test/lib/angular/angular-mocks.js:1171:34)
at null.<anonymous> (/Users/me/app/test/unit/controllers/ExampleListCtrlSpec.js:14:18)
---- Edited ----
Following the advice below, I have moved the controller out of beforeEach, and changed test like this, so that I can test $httpBackend.expectGET more than once.
describe('ExampleListCtrl', function(){
var $scope, $rootScope, $httpBackend;
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
controller = $controller;
$scope = $rootScope.$new();
}));
it('should create "examples" model with 0 example fetched from xhr', function() {
$httpBackend.expectGET('examples').respond([]);
controller(ExampleListCtrl, {$scope: $scope});
$httpBackend.flush();
expect($scope.examples).toEqual([]);
});
});
This happens because once you instantiate your controller, it fetches $http and, thus, $httBackend. So, all expectations shall be made prior to requiring it.
If you wish to move your $httpBackend.expectGet to the spec (it block) then you need to move your $controller(ExempleListCtrl, ... as well, after the expectations.