Idiomatic composition of $http and $httpBackend in AngularJS 1.2.2 - javascript

I have an AngularJS/Jasmine unit test that works fine with AngularJS 1.0.7, but doesn't work in Angular 1.2.2:
servicesSpec.js
describe('services', function() {
beforeEach(module('workPadApp'));
describe('taskGateway', function() {
var sut;
beforeEach(inject(function(taskGateway) {
sut = taskGateway;
}));
describe('saving a task', function() {
it('GETs correctly', inject(function($httpBackend) {
$httpBackend.expectGET('/').respond({ foo : 'bar' });
sut.createTask({ taskRef : 'baz' });
$httpBackend.flush();
}))
})
})
})
app.js
var workPadApp = angular.module('workPadApp', ['workPadApp.services']);
services.js
angular.module('workPadApp.services', []).
factory('taskGateway', function($http, $q) {
return {
createTask : function(task) {
$http.get('/')
.success(function(response) {
console.log('success');
})
.error(function(response) {
console.log('error');
});
}
}
});
Expected result
When I run this with Karma using AngularJS 1.0.7, all is good:
LOG: 'success'
Chrome 33.0.1750 (Windows 7): Executed 1 of 1 SUCCESS (0.179 secs / 0.03 secs)
Actual result
However, when I run it with AngularJS 1.2.2, I get this output:
Chrome 33.0.1750 (Windows 7) services taskGateway saving a task GETs correctly FAILED
Error: No pending request to flush !
at Error (native)
at Function.$httpBackend.flush (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/lib/angular/angular-mocks.js:1195:34)
at null.<anonymous> (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/unit/servicesSpec.js:91:18)
at Object.invoke (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/app/lib/angular.js:3641:28)
at workFn (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/lib/angular/angular-mocks.js:1778:20)
Error: Declaration Location
at window.jasmine.window.inject.angular.mock.inject (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/lib/angular/angular-mocks.js:1764:25)
at null.<anonymous> (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/unit/servicesSpec.js:72:26)
at null.<anonymous> (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/unit/servicesSpec.js:62:3)
at null.<anonymous> (c:/Users/Mark/Documents/Grean/HHM/Src/HHMFrontend/test/unit/servicesSpec.js:8:2)
Chrome 33.0.1750 (Windows 7): Executed 1 of 1 (1 FAILED) (0.349 secs / 0.133 secs)
I've looked at the documentation for $httpBackend, and the example there uses a much more imperative style of setup using an $injector service.
That surprises me, since the declarative style that worked in version 1.0.7 is much terser. Is this really the only correct way going forward, or am I missing something?

As it turns out the relevant angular-* modules must be the same version.
In this example, angular.js and angular-mocks.js had to be the same version, 1.2.2.

Related

Angular Unit Test Failing Expected spy

I have below controller to get the books list and single books detail. It's working as expected but the unit test is not working as expected.
books.controller.js
var myApp = angular.module('myApp');
function BooksController($log, $routeParams, BooksService) {
// we declare as usual, just using the `this` Object instead of `$scope`
const vm = this;
const routeParamId = $routeParams.id;
if (routeParamId) {
BooksService.getBook(routeParamId)
.then(function (data) {
$log.info('==> successfully fetched data for book id:', routeParamId);
vm.book = data;
})
.catch(function (err) {
vm.errorMessage = 'OOPS! Book detail not found';
$log.error('GET BOOK: SOMETHING GOES WRONG', err)
});
}
BooksService.getBooks()
.then(function (data) {
$log.info('==> successfully fetched data');
vm.books = data;
})
.catch(function (err) {
vm.errorMessage = 'OOPS! No books found!';
$log.error('GET BOOK: SOMETHING GOES WRONG', err)
});
}
BooksController.$inject = ['$log', '$routeParams', 'BooksService'];
myApp.controller('BooksController', BooksController);
Spec for above controller in which I want to test the getBook(id) service but somehow I am not able to pass the id of book.
describe('Get All Books List: getBooks() =>', () => {
const errMsg = 'OOPS! No books found!';
beforeEach(() => {
// injecting rootscope and controller
inject(function (_$rootScope_, _$controller_, _$q_, BooksService) {
$scope = _$rootScope_.$new();
$service = BooksService;
$q = _$q_;
deferred = _$q_.defer();
// Use a Jasmine Spy to return the deferred promise
spyOn($service, 'getBooks').and.returnValue(deferred.promise);
// The injector unwraps the underscores (_) from around the parameter names when matching
$vm = _$controller_('BooksController', {$scope: $scope, $service: BooksService});
});
});
it('should defined getBooks $http methods in booksService', () => {
expect(typeof $service.getBooks).toEqual('function');
});
it('should able to fetch data from getBooks service', () => {
// Setup the data we wish to return for the .then function in the controller
deferred.resolve([{ id: 1 }, { id: 2 }]);
// We have to call apply for this to work
$scope.$apply();
// Since we called apply, now we can perform our assertions
expect($vm.books).not.toBe(undefined);
expect($vm.errorMessage).toBe(undefined);
});
it('should print error message if data not fetched', () => {
// Setup the data we wish to return for the .then function in the controller
deferred.reject(errMsg);
// We have to call apply for this to work
$scope.$apply();
// Since we called apply, now we can perform our assertions
expect($vm.errorMessage).toBe(errMsg);
});
});
describe('Get Single Book Detail: getBook() =>', () => {
const errMsg = 'OOPS! Book detail not found';
const routeParamId = '59663140b6e5fe676330836c';
beforeEach(() => {
// injecting rootscope and controller
inject(function (_$rootScope_, _$controller_, _$q_, BooksService) {
$scope = _$rootScope_.$new();
$scope.id = routeParamId;
$service = BooksService;
$q = _$q_;
var deferredSuccess = $q.defer();
// Use a Jasmine Spy to return the deferred promise
spyOn($service, 'getBook').and.returnValue(deferredSuccess.promise);
// The injector unwraps the underscores (_) from around the parameter names when matching
$vm = _$controller_('BooksController', {$scope: $scope, $service: BooksService});
});
});
it('should defined getBook $http methods in booksService', () => {
expect(typeof $service.getBook).toEqual('function');
});
it('should print error message', () => {
// Setup the data we wish to return for the .then function in the controller
deferred.reject(errMsg);
// We have to call apply for this to work
$scope.$apply();
// expect($service.getBook(123)).toHaveBeenCalled();
// expect($service.getBook(123)).toHaveBeenCalledWith(routeParamId);
// Since we called apply, now we can perform our assertions
expect($vm.errorMessage).toBe(errMsg);
});
});
"Get Single Book Detail: getBook()" this suit is not working. Please help me, how to short out this kind of situation.
Error I am getting is below
Chrome 59.0.3071 (Mac OS X 10.12.5) Books Controller Get Single Book Detail: getBook() => should print error message FAILED
Expected 'OOPS! No books found!' to be 'OOPS! Book detail not found'.
Chrome 59.0.3071 (Mac OS X 10.12.5) Books Controller Get Single Book Detail: getBook() => should print error message FAILED
Expected 'OOPS! No books found!' to be 'OOPS! Book detail not found'.
at Object.it (test/client/controllers/books.controller.spec.js:108:38)
Chrome 59.0.3071 (Mac OS X 10.12.5): Executed 7 of 7 (1 FAILED) (0 secs / 0.068 secs)
.
Chrome 59.0.3071 (Mac OS X 10.12.5): Executed 7 of 7 (1 FAILED) (0.005 secs / 0.068 secs)
you need to mock $rootScope. with provide.
The value of id is not getting avaibale in controller which is undefined.
So, non-id condition getting executing.
$scope = _$rootScope_.$new();
$scope.id = routeParamId;
module(function ($provide) {
$provide.value('$rootScope', scope); //mock rootscope with id
});
Real router should never be used in unit tests, with ngRoute module preferably be excluded from tested modules.
$scope.id = routeParamId is assigned before controller instantiation, but it isn't used at all. Instead, it should be done with mocked $routeParams.
There's no $service service. It's called BooksService. Thus getBooks isn't a spy. It's preferable to mock the service completely, not only a single method.
mockedBooksService = jasmine.createSpyObj('BooksService', ['getBooks']);
var mockedData1 = {};
var mockedData2 = {};
mockedBooksService.getBooks.and.returnValues(
$q.resolve(mockedData1),
$q.resolve(mockedData2),
);
$vm = $controller('BooksController', {
$scope: $scope,
BooksService: mockedBooksService,
$routeParams: { id: '59663140b6e5fe676330836c' }
});
expect(mockedBooksService.getBooks).toHaveBeenCalledTimes(2);
expect(mockedBooksService.getBooks.calls.allArgs()).toEqual([
['59663140b6e5fe676330836c'], []
]);
$rootScope.$digest();
expect($vm.book).toBe(mockedData2);
// then another test for falsy $routeParams.id
The test reveals the problem in controller code. Since tested code is called on controller construction, $controller should be called every time in it. A good way to avoid this is to put initialization code into $onInit method that could be tested separately.
EDIT (removed original, 2am answer)
Are you using strict mode? There appear to be a few scoping issues going on:
On line 9 (in the "Get All Books List" spec), deferred is not declared, making it global implicitly
The last test ran on the "Get All Books List" spec fails the global deferred promise
On line 60 (in the "Get Single Book Detail" spec), deferredSuccess is declared with var making it local to the function passed to inject()
On line 70 (the test in question), where (I assume) you meant to reject the "Single Book" deferredSuccess, you're actually failing the global/list deferred promise. This has no effect, since as mentioned in item 2 that promise was already failed and Q ignores repeated rejections.
So, that should explain why the error is not what you think it should be.
deferred isn't the only variable with scoping issues in your example; those should be addressed. I suggest wrapping the file in an IFFE and using strict mode. It'll make the code more predictable and avoid issues like this.
Doing this will only get you halfway there; #estus's response should round out the job.

AssertionError { state: 'pending' }

I try to use a compilation of differents stacks:
Mocha – test runner
Chai – assertion library
webdriverio – browser control bindings
Selenium – browser abstraction and running factory
PhantomJS – fast headless browser
so i launch a selenium server like this
java -jar selenium-server.jar
and i launch my test like this
mocha test.js -t 10000
Here is my test.js
var webdriverio = require('webdriverio');
var options = { desiredCapabilities: { browserName: 'phantomjs' } };
var client = webdriverio.remote(options);
describe('Test example.com', function(){
before(function(done) {
client.init().url('/* my website */');
done();
//client.pause(5000);
var chai = require('chai');
global.expect = chai.expect;
chai.Should();
});
describe('Check homepage', function(){
it('should wait 3 secondes', function() {
client.pause(3000);
});
it('should see the correct title', function() {
client.waitForValue('#logoHeaderNav', 3000);
client.url('/* my website */');
client.getTitle().should.be.equal('/*my title*/');
});
});
after(function(done) {
client.end();
done();
});
});
and the result i get is :
# mocha test.js -t 10000
Test example.com
Check homepage
✓ should wait 3 secondes
1) should see the correct title
1 passing (108ms)
1 failing
1) Test example.com Check homepage should see the correct title:
AssertionError: expected { state: 'pending' } to equal '/*my title */'
at Context.<anonymous> (test.js:90:35)
any ideas of something i'm doing wrong ??
WebdriverIO commands all return promises, which is what the { state: 'pending' } is in your error message.
To get around this, you'll want to use Chai's "as-promised" plugin. The official site has a page detailing how to get this set up for you.
Try removing the client.waitForValue('#logoHeaderNav', 3000); statement and see if it works.

Using StackTrace.js with Angular.js

I'm implementing a logger for my whole MEAN.js project. I have a server side logger that works well, and I've set an endpoint to receive a POST request with an exception to log a client error. I'm using StackTrace.js for client side error logging, but I'm getting some errors; now I'm using StackTrace's error-stack-parser and still getting errors. I'm using a decorator on Angular's $exceptionHandler to achieve this:
$provide.decorator("$exceptionHandler",
function($delegate, traceService, $log, $window) {
return function(exception, cause) {
$delegate(exception, cause);
try {
var errorMessage = exception.toString();
var stackTrace = traceService.
parse(exception).
then(function(stackFrames) {
$.ajax({
type: 'POST',
url: '/logger',
contentType: 'application/json',
data: angular.toJson({
url: $window.location.href,
message: errorMessage,
type: 'exception',
stackFrace: stackFrames,
cause: cause || ''
})
}).fail(function() {
$log.warn('POST request failed');
});
}).catch(function(error) {
$log.warn('Error StackTrace');
});
} catch (loggingError) {
$log.warn('Error server-side logging failed');
$log.log(loggingError);
}
};
});
traceService is an Angular service that acts as a proxy for StackTrace/error-stack-parser functions. traceService.parse is equivalent to ErrorStackParser.parse. traceService is implemented as:
angular.module('core').factory('traceService', function() {
return {
parse: ErrorStackParser.parse
};
});
I've put this decorator code on the Angular app bootstrap, and I'm testing by throwing an error on a controller. When I run the app I get this error on the client console:
Error server-side logging failed
angular.js:13708 TypeError: this.parseV8OrIE is not a function
at Object.ErrorStackParser$$parse [as parse] (error-stack-parser.js:70)
at application.js:22
at invokeLinkFn (angular.js:9816)
at nodeLinkFn (angular.js:9215)
at compositeLinkFn (angular.js:8510)
at publicLinkFn (angular.js:8390)
at lazyCompilation (angular.js:8728)
at updateView (viewDirective.ts:278)
at Object.configUpdatedCallback [as configUpdated] (viewDirective.ts:226)
at configureUiView (view.ts:164)
It would seem like this is an issue with error-stack-parse; I can't seem to find any articles regarding this issue, so I'm sure it's something I'm doing wrong, the way I'm testing or something else. Can any provide some insight as to why this is failing?
Edit
I modified the code following Caramiriel's comment. It seems like I need to add all functions from error-stack-parser to my service. This is traceService:
angular.module('core').factory('traceService', function() {
return {
parse: ErrorStackParser.parse,
parseV8OrIE: ErrorStackParser.parseV8OrIE,
extractLocation: ErrorStackParser.extractLocation
};
});
Now I'm getting this error:
TypeError: StackFrame is not a constructor
at Object.<anonymous> (http://localhost:3000/lib/error-stack-parser/error-stack-parser.js:105:24)
at Array.map (native)
at _map (http://localhost:3000/lib/error-stack-parser/error-stack-parser.js:22:26)
at Object.ErrorStackParser$$parseV8OrIE [as parseV8OrIE] (http://localhost:3000/lib/error-stack-parser/error-stack-parser.js:95:20)
at Object.ErrorStackParser$$parse [as parse] (http://localhost:3000/lib/error-stack-parser/error-stack-parser.js:70:29)
at http://localhost:3000/application.js:22:11
at invokeLinkFn (http://localhost:3000/lib/angular/angular.js:9816:9)
at nodeLinkFn (http://localhost:3000/lib/angular/angular.js:9215:11)
at compositeLinkFn (http://localhost:3000/lib/angular/angular.js:8510:13)
at publicLinkFn (http://localhost:3000/lib/angular/angular.js:8390:30)
It looks like you are using error-stack-parser without its dependency on the stackframe project.
You'll get that dependency automatically if you use npm or bower.

Jasmine test fails making $httpbackend call due to angular mock receiving incorrect json format

I'm having trouble writing what I think should be a simple unit test. I have the following controller:
(function(){ 'use strict';
var LoginController = function($scope, $state, RestService){
var _user = {};
var _message = 'hello';
var _login = function(username, password){
var _success = function(response){
_message = response.success;
_user = response.user;
};
var _error = function(response){
_message = response.success;
};
RestService.postData('/api/login', {username: username, password: password}, _success, _error, {showLoader: true});
};
$scope.model = {
login: _login,
user: _user,
message: _message
};
};
angular.module('danny').controller('LoginController',['$scope', '$state', 'RestService',LoginController]);
})();
Here is the spec:
describe('LoginController', function(){
var scope, $httpBackend, controller, restService;
beforeEach(function(){
module('danny');
});
beforeEach(inject(function(_$controller_, _$rootScope_, _$httpBackend_, _RestService_){
$httpBackend = _$httpBackend_;
restService = _RestService_;
scope = _$rootScope_.$new();
controller = _$controller_('LoginController', {
$scope: scope,
RestService: restService
});
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('successfully logging in', function(){
it('should redirect to /blog when authenticated', function(){
var user = {"username":"danny#ravenartmedia.com", "password":"test"};
expect(user.username).toEqual('danny#ravenartmedia.com');
$httpBackend.expectPOST('/api/login', user);
scope.model.login(user);
$httpBackend.flush();
expect(scope.model.user).not.toBe(undefined);
});
});
});
When I run the test the karma output is this:
C:\Program Files (x86)\JetBrains\WebStorm 9.0.1\bin\runnerw.exe" C:\nodejs\node.exe c:\Users\danny_000\AppData\Roaming\npm\node_modules\grunt-cli\bin\grunt test
Running "karma:development" (karma) task
INFO [karma]: Karma v0.12.16 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Windows 8)]: Connected on socket SWVDLzehlqv2Z3J0C2Av with id 62852294
PhantomJS 1.9.8 (Windows 8): Executed 0 of 1 SUCCESS (0 secs / 0 secs)
PhantomJS 1.9.8 (Windows 8) LoginController successfully logging in should redirect to /blog when authenticated FAILED
SyntaxError: Unable to parse JSON string
at fromJson (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:1066)
at c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1646
at $httpBackend (c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1194)
at sendReq (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:9616)
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:9331
at processQueue (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:13171)
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:13187
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14384
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14200
at c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1525
at c:/Projects/dannyschreiber/public/src/core/security/login-controller.spec.js:6
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.3.9/$rootScope/inprog?p0=%24digest
at beginPhase (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14738)
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14180
at c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1557
at c:/Projects/dannyschreiber/public/src/core/security/login-controller.spec.js:6
PhantomJS 1.9.8 (Windows 8): Executed 1 of 1 (1 FAILED) (0 secs / 0.031 secs)
PhantomJS 1.9.8 (Windows 8): Executed 1 of 1 (1 FAILED) ERROR (0.028 secs / 0.031 secs)
Warning: Task "karma:development" failed. Use --force to continue.
I ended up commenting out the angular-mocks line that was causing the error (line# 1646), and see that for some reason, somewhere, the data i'm sending is being formatted incorrectly. This is the error output now:
Running "karma:development" (karma) task
INFO [karma]: Karma v0.12.16 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Windows 8)]: Connected on socket xGP0gRaa3WEdNVw1DrQm with id 89049214
PhantomJS 1.9.8 (Windows 8): Executed 0 of 1 SUCCESS (0 secs / 0 secs)
PhantomJS 1.9.8 (Windows 8) LoginController successfully logging in should redirect to /blog when authenticated FAILED
Error: Expected POST /api/login with different data
EXPECTED: {"username":"danny#ravenartmedia.com","password":"test"}
GOT: username=danny%40ravenartmedia.com&password=test
at $httpBackend (c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1196)
at sendReq (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:9616)
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:9331
at processQueue (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:13171)
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:13187
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14384
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14200
at c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1525
at c:/Projects/dannyschreiber/public/src/core/security/login-controller.spec.js:6
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.3.9/$rootScope/inprog?p0=%24digest
at beginPhase (c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14738)
at c:/Projects/dannyschreiber/public/vendors/angular/angular.js:14180
at c:/Projects/dannyschreiber/public/vendors/angular-mocks/angular-mocks.js:1557
at c:/Projects/dannyschreiber/public/src/core/security/login-controller.spec.js:6
PhantomJS 1.9.8 (Windows 8): Executed 1 of 1 (1 FAILED) (0 secs / 0.032 secs)
PhantomJS 1.9.8 (Windows 8): Executed 1 of 1 (1 FAILED) ERROR (0.029 secs / 0.032 secs)
Warning: Task "karma:development" failed. Use --force to continue.
The key piece of info being:
Error: Expected POST /api/login with different data
EXPECTED: {"username":"danny#ravenartmedia.com","password":"test"}
GOT: username=danny%40ravenartmedia.com&password=test
Why is my json being altered?
Here is the postData function from the RestService, which I've used in many projects without issues:
var postData = function(url, params, data, successFunction, errorFunction, config) {
if(config && config.hasOwnProperty('showLoader')){
$rootScope.showLoader = config.showLoader;
}
$http({
method: 'POST',
url: url,
params: params,
data: data,
cache: false
})
.success(function(data, status, headers, config) {
$rootScope.showLoader = false;
if (successFunction === undefined) {
_defaultSuccessFunction(data, status, headers, config);
}
else {
successFunction(data, status, headers, config);
}
})
.error(function (data, status, headers, config) {
$rootScope.showLoader = false;
if (status !== 0){
_processError(data, status, headers, config, errorMsg, errorFunction);
}
});
};
Edit:
I was able to hook all of this up in plnkr: http://plnkr.co/edit/lJTx0ldR9nEnlYbU5pJd and the test passes....but the exact same code gives me the error I'm referring to in this post. I'm running v. 1.3.9 of both Angular and Angular-Mocks.
Your helper method is defined as function(url, params, data, successFunction, errorFunction, config), where data is the third parameter.
Compare this to your invocation:
RestService.postData('/api/login', null, null, {username: username, password: password}, _success, 'Invalid login, please try again', _error, {showLoader: true});
Here you pass your data as the fourth parameter. So I think you want to remove one of the null values. Unfortunately Javscript does not warn about wrong amounts of parameters (just ignores them).
Edit:
Meanwhile you have edited your question, however your invocation of postData is probably still incorrect.
I've prepared a fiddle using your code: http://jsfiddle.net/qwteyak3/1/
The first invocation of postData gets the data as the params parameter. This tells $http to send it as form data. The second invocation passes it in the data field. $http checks data, finds an object and then sends it as a JSON-body.
When running the fiddle, Chrome's network tab shows me a request to http://fiddle.jshell.net/echo/json?password=123&username=test for the first invocation and http://fiddle.jshell.net/echo/json (with data in body) for the second.
I hope this helps.

Angular resource testing: $httpBackend.flush() cause Unexpected request

I want to test angularjs resource.
'use strict';
/**
* AddressService provides functionality to use address resource in easy way.
*
* This is an example usage of method:
*
* `get`:
*
var a = AddressService.get({id: '1'},
function (data) {
// Work here with your resource
}
);
*
*/
App.factory('AddressService', function ($resource, $rootScope) {
var url = [
$rootScope.GLOBALS.API_PATH,
$rootScope.GLOBALS.API_VERSION,
'models/address/:id'
].join('/'),
actions = {
'get': {
method: 'GET',
params: {id: '#id'}
}
};
return $resource(url, {}, actions);
});
I created the test:
'use strict';
var $httpBackend;
describe('Service: AddressService', function () {
beforeEach(module('myApp'));
beforeEach(inject(function ($injector) {
var url_get = 'api/v1/models/address/5';
var response_get = {
address_line_1: '8 Austin House Netly Road',
address_line_2: 'Ilford IR2 7ND'
};
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET(url_get).respond(response_get);
}));
describe('AddressService get test', function () {
it('Tests get address', inject(function (AddressService) {
var address = AddressService.get({ id: '5'});
$httpBackend.flush();
expect(address.address_line_1).toEqual('8 Austin House Netly Road');
expect(address.address_line_2).toEqual('Ilford IR2 7ND');
}));
});
});
I am not experienced with angular very well.
I have set jasmine in karma.config.js.
AngularJS v1.0.6
Yoeman and Grunt manage project.
I try grunt test
Running "karma:unit" (karma) task
INFO [karma]: Karma server started at http://localhost:8080/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 27.0 (Linux)]: Connected on socket id RFFUY5bW8Hb5eTu0n-8L
Chrome 27.0 (Linux) Service: AddressService AddressService get Tests get address FAILED
Error: Unexpected request: GET views/home.html
No more request expected
at Error (<anonymous>)
at $httpBackend (/home/bart/y/projects/x/frontend/app/components/angular-mocks/angular-mocks.js:910:9)
at sendReq (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:9087:9)
at $http (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:8878:17)
at Function.$http.(anonymous function) [as get] (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:9021:18)
at $q.when.then.then.next.locals (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:7394:34)
at wrappedCallback (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:6797:59)
at wrappedCallback (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:6797:59)
at /home/bart/y/projects/x/frontend/app/components/angular/angular.js:6834:26
at Object.Scope.$eval (/home/bart/y/projects/x/frontend/app/components/angular/angular.js:8011:28)
Error: Declaration Location
at window.jasmine.window.inject.angular.mock.inject (/home/bart/y/projects/x/frontend/app/components/angular-mocks/angular-mocks.js:1744:25)
at null.<anonymous> (/home/bart/y/projects/x/frontend/test/spec/services/userdata/AddressService.js:32:30)
at null.<anonymous> (/home/bart/y/projects/x/frontend/test/spec/services/userdata/AddressService.js:31:5)
at /home/bart/y/projects/x/frontend/test/spec/services/userdata/AddressService.js:5:1
..................................................................
Chrome 27.0 (Linux): Executed 67 of 67 (1 FAILED) (0.343 secs / 0.179 secs)
Warning: Task "karma:unit" failed. Use --force to continue.
If I remove $httpBackend.flush() in test. Test is passing. But I am getting undefined from address. I saw examples: example all use the flush() But only I get silly exception: Error: Unexpected request: GET views/home.html
views/home.html is my view in project directory. I have no idea how solve my problem I could not find any solution. Am I missing the point somehow?
Can anybody see mistake in my code? All suggestions will be appreciate.
EDIT
I have found that I need to use this:
$httpBackend.when('GET', /\.html$/).passThrough();
But another problem is I am getting:
TypeError: Object #<Object> has no method 'passThrough'
the error is caused by your test attempting to load the main page for your app. Since you have not told the test to expect this server call, an error is returned. See the documentation for $httpBackend for clarification on this point.
Your $templateCache workaround is designed for unit testing directives not anything else. You were quite close with:
$httpBackend.when('GET', /\.html$/).passThrough();
Since you don't need to do anything with the actual template file it's a safe and simple work around to add this to your beforeEach() block.
$httpBackend.whenGET(/\.html$/).respond('');
This stops you getting the TypeError you had.
TypeError: Object #<Object> has no method 'passThrough'
I had the same issue in my unit tests after upgrading to a new version of Angular JS and using respond('') worked fine.
I have finally found solution:
I tried to follow this: other post
So I added $templateCache:
'use strict';
var $httpBackend;
describe('Service: AddressService', function () {
beforeEach(module('myApp'));
beforeEach(inject(function ($injector, $templateChache) {
$templateCache.put('views/home.html', '.<template-goes-here />');
var url_get = 'api/v1/models/address/5';
var response_get = {
address_line_1: '8 Austin House Netly Road',
address_line_2: 'Ilford IR2 7ND'
};
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET(url_get).respond(response_get);
}));
describe('AddressService get test', function () {
it('Tests get address', inject(function (AddressService) {
var address = AddressService.get({ id: '5'});
$httpBackend.flush();
expect(address.address_line_1).toEqual('8 Austin House Netly Road');
expect(address.address_line_2).toEqual('Ilford IR2 7ND');
}));
});
});

Categories

Resources