Mock promises in angularjs - javascript

I tried to mocking angular promises but I got some errors like undefined is not a function evaluating 'spyOn' fileUploadService
My controller code is
$scope.getUserFiles = function() {
fileUploadService.retrieveUserFileNames('')
.then(function(data) {
$scope.userFiles = data;
});
};
service code, I call this method from my controller
this.retrieveUserFileNames= function(userId) {
var deferred = $q.defer();
$http({
method : "GET",
url : "/retrieveExcelSheetNames",
params : {
"userId" : userId
}
}).success(function(data) {
deferred.resolve(data);
}).error(function(data, status) {
deferred.reject(data);
});
return deferred.promise;
};
test controller code
beforeEach(function() {
inject(function(_fileUploadService_ , _$q_) {
spyOn(scope, '$on');
var deferred = _$q_.defer();
fileUploadService = _fileUploadService_;
deferred.resolve('resolveData');
spyOn(fileUploadService, 'retrieveUserFileNames').andReturn(deferred.promise);
});
});
it('is now a lot easier', function() {
scope.getUserFiles();
rootScope.$apply();
expect(scope.userFiles).toBe('resolveData');
});
Thanks

As you're asynchronously set $scope.userFiles in your controller so you need to wait for the $apply from $http that resolved your promise.
Assuming you're using Jasmine you could use the asynchronous spec notation and register a $watch function to test your controller (Didn't test the code so you might need to tune it and it also depends on other modifications of the scope variable).
it('is now a lot easier', function(done) {
scope.$watch('userFiles', function() {
expect(scope.userFiles).toBe('resolveData');
// Call done function to tell Jasmine that the spec has completed
done();
});
scope.getUserFiles();
});
However, I think you should rather test your service and you can use $httpBackend from ngMock which is overriding $http and enables you to test much more in-depth. There you can test / spy the calls to the mock backend and also use Jasmines done() function to wait for the promise.
Here is a simple exmaple:
angular.module('mainApp', [])
// Simple factory that uses $http for later use of $httpBackend mocking structure
.factory('userManager', function($http, $q) {
return $http.get('/api/users');
});
And the specs:
describe('Simple factory that uses $http for later use of $httpBackend mocking structure', function() {
// We need to use the module mainApp for all our tests
beforeEach(module('mainApp'));
// We use $httpBackend from ngMock module to mock our webservice call ($http will be overriden)
// Also we need to inject inside of spec function as we need to use the async spec form with
// a done function and this is not available using the inject to proxy the spec function
it('should return desired user list', function(done) {
inject(function($httpBackend, userManager) {
$httpBackend.when('GET', '/api/users').respond({
userList: [
{user: 'User A'},
{user: 'User B'},
{user: 'User C'},
{user: 'User D'},
]
});
$httpBackend.expectGET('/api/users');
// userManager is returning a promise so we need to check asynchronously
userManager.getUserList().then(function(result) {
expect(result.data.userList).toContain(
{user: 'User A'},
{user: 'User B'},
{user: 'User C'},
{user: 'User D'}
);
// This call to the Jasmine done function is very important for asynchronous
// unit tests as Jasmine is determining if the test is done by this call
done();
});
$httpBackend.flush();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});
});

Related

Testing angular $http inside promise chain with mocha

I have jsdom/mocha/chai set up for backend angular testing.
I have a service that essentially does this (intentionally no post data):
app.service('testService', ['config', '$http', function(config, $http) {
function getSpecificConfig(type) {
return config.getConfig()
.then(function(config) {
// config is coming back defined;
// $http timesout
return $http({method: 'post', url: 'http://localhost:2222/some/path', withCredentials: true});
})
.then(function(res) {
return res.data.config[type];
})
.catch(function(err) {
//handles err
});
};
return {
getConfig: getConfig
}
}]);
my test is:
/* jshint node: true */
/* jshint esversion: 6 */
let helpers = require(bootstrapTest),
inject = helpers.inject,
config,
specificConfig,
mockResponse,
$httpBackend,
$rootScope;
//config service
require('config.js');
//testService I'm testing
require('testService');
beforeEach(inject(function($injector, _$httpBackend_) {
config = $injector.get('config');
specificConfig = $injector.get('testService');
$rootScope = $injector.get('$rootScope');
$httpBackend = _$httpBackend_;
$httpBackend.when('POST', 'http://localhost:2222/some/path')
.response(function(data) {
//would like this to fire
console.log('something happened');
mockResponse = {data: 'some data'};
return mockResponse;
});
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectations();
$httpBackend.verifyNoOutstandingRequest();
});
describe ('this service', function() {
beforeEach(function() {
$httpBackend.expect('POST', 'http://localhost:2222/some/path');
$rootScope.$apply(function() {
return specificConfig('something');
});
});
it ('returns the specific config', function() {
expect(mockResponse).to.equal('some data');
})
});
Problem:
When the test is run, the config.getConfig() is resolving properly but the $http leads to a mocha timeout (2000ms) and the afterEach hook throws an Unsatisfied request.
My understanding of this may be completely incorrect so please feel free to educate me on the correct approach (here was my approach):
1) require all necessary dependencies.
2)inject them and set up a $httpBackend listener which fires the test response when the real http is fired.
3) $rootScope.$apply() any promises as the resolution of them is tied to the angular lifecycle.
4) the first before each sets the listener, the second before each fires the service which fires the $http allowing $httpBackend to fire and set the mockResponse.
5) test mock response.
If you need to return promises in your mocked HTTP requests you can use angular-mocks-async like so:
var app = ng.module( 'mockApp', [
'ngMockE2E',
'ngMockE2EAsync'
]);
app.run( [ '$httpBackend', '$q', function( $httpBackend, $q ) {
$httpBackend.whenAsync(
'GET',
new RegExp( 'http://api.example.com/user/.+$' )
).respond( function( method, url, data, config ) {
var re = /.*\/user\/(\w+)/;
var userId = parseInt(url.replace(re, '$1'), 10);
var response = $q.defer();
setTimeout( function() {
var data = {
userId: userId
};
response.resolve( [ 200, "mock response", data ] );
}, 1000 );
return response.promise;
});
}]);

service promise not resolving with karma

I have a service factory that connects and interacts with an api for data. Here is the service:
angular.module('dbRequest', [])
.factory('Request', ['$http', 'localConfig', function($http, localConfig){
return {
getDataRevision: function(){
return $http({
url: localConfig.dbDataUrl,
method: "GET",
crossDomain: true,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
})
}
}
}]);
Taking cues from this answer, This is how I am testing the method:
describe('Service: Request', function () {
var $scope, srvMock, $q, lc, $httpBackend;
beforeEach(module('webApp'));
beforeEach(inject(function($rootScope, Request, _$q_, _$httpBackend_, localConfig){
$scope = $rootScope.$new();
srvMock = Request;
$q = _$q_;
$httpBackend = _$httpBackend_;
lc = localConfig;
$httpBackend.expect('GET', lc.dbDataUrl);
$httpBackend.when('GET', lc.dbDataUrl).respond({
success: ["d1","d2", "d3"]
});
}));
it('should return a promise', function(){
expect(srvMock.getDataRevision().then).toBeDefined();
});
it('should resolve with data', function(){
var data;
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(function(res){
data = res.success;
});
srvMock.getDataRevision().then(function(res){
deferred.resolve(res);
});
$scope.$digest();
expect(data).toEqual(["d1","d2", "d3"]);
})
});
should return a promise passes, but the next should resolve with data fails with this error:
Expected undefined to equal [ 'd1', 'd2', 'd3' ].
However, the service method getDataRevision is getting called, but not getting resolved by the mock promise in the test. How do I make the correction?
Currently you are expecting mocked data to be there in data variable without flushing httpRequests, but that won't happen till you flush all the httpRequests. What $httpBackend.flush() does is, it returns mock data to that particular request that you have did using $httpBackend.when('GET', lc.dbDataUrl).respond.
Additionally you don't need to create extra promise which would be an overhead. Instead of having custom promise you could have utilize service function returned promise itself like below.
Code
it('should resolve with data', function(){
var data;
srvMock.getDataRevision().then(function(res){
data = res.success;
});
$scope.$digest();
$httpBackend.flush(); //making sure mocked response has been return
//after .then evaluation only below code will get called.
expect(data).toEqual(["d1","d2", "d3"]);
})

How should the run block be dealt with in Angular unit tests?

My understanding is that when you load your module in Angular unit tests, the run block gets called.
I'd think that if you're testing a component, you wouldn't want to simultaneously be testing the run block, because unit tests are supposed to just test one unit. Is that true?
If so, is there a way to prevent the run block from running? My research leads me to think that the answer is "no", and that the run block always runs when the module is loaded, but perhaps there's a way to override this. If not, how would I test the run block?
Run block:
function run(Auth, $cookies, $rootScope) {
$rootScope.user = {};
Auth.getCurrentUser();
}
Auth.getCurrentUser:
getCurrentUser: function() {
// user is logged in
if (Object.keys($rootScope.user).length > 0) {
return $q.when($rootScope.user);
}
// user is logged in, but page has been refreshed and $rootScope.user is lost
if ($cookies.get('userId')) {
return $http.get('/current-user')
.then(function(response) {
angular.copy(response.data, $rootScope.user);
return $rootScope.user;
})
;
}
// user isn't logged in
else {
return $q.when({});
}
}
auth.factory.spec.js
describe('Auth Factory', function() {
var Auth, $httpBackend, $rootScope, $cookies, $q;
var user = {
username: 'a',
password: 'password',
};
var response = {
_id: 1,
local: {
username: 'a',
role: 'user'
}
};
function isPromise(el) {
return !!el.$$state;
}
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('#signup', function() {
$rootScope.user = {};
$httpBackend.expectPOST('/users', user).respond(response);
spyOn(angular, 'copy').and.callThrough();
spyOn($cookies, 'put').and.callThrough();
var retVal = Auth.signup(user);
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith(response, $rootScope.user);
expect($cookies.put).toHaveBeenCalledWith('userId', 1);
expect(isPromise(retVal)).toBe(true);
});
it('#login', function() {
$rootScope.user = {};
$httpBackend.expectPOST('/login', user).respond(response);
spyOn(angular, 'copy').and.callThrough();
spyOn($cookies, 'put').and.callThrough();
var retVal = Auth.login(user);
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith(response, $rootScope.user);
expect($cookies.put).toHaveBeenCalledWith('userId', 1);
expect(isPromise(retVal)).toBe(true);
});
it('#logout', function() {
$httpBackend.expectGET('/logout').respond();
spyOn(angular, 'copy').and.callThrough();
spyOn($cookies, 'remove');
Auth.logout();
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith({}, $rootScope.user);
expect($cookies.remove).toHaveBeenCalledWith('userId');
});
describe('#getCurrentUser', function() {
it('User is logged in', function() {
$rootScope.user = response;
spyOn($q, 'when').and.callThrough();
var retVal = Auth.getCurrentUser();
expect($q.when).toHaveBeenCalledWith($rootScope.user);
expect(isPromise(retVal)).toBe(true);
});
it('User is logged in but page has been refreshed', function() {
$cookies.put('userId', 1);
$httpBackend.expectGET('/current-user').respond(response);
spyOn(angular, 'copy').and.callThrough();
var retVal = Auth.getCurrentUser();
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith(response, $rootScope.user);
expect(isPromise(retVal)).toBe(true);
});
it("User isn't logged in", function() {
$rootScope.user = {};
$cookies.remove('userId');
spyOn($q, 'when').and.callThrough();
var retVal = Auth.getCurrentUser();
expect($q.when).toHaveBeenCalledWith({});
expect(isPromise(retVal)).toBe(true);
});
});
});
Attempt 1:
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
beforeEach(function() {
spyOn(Auth, 'getCurrentUser');
});
afterEach(function() {
expect(Auth.getCurrentUser).toHaveBeenCalled();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
This doesn't work. The run block is run when the module is loaded, so Auth.getCurrentUser() is called before the spy is set up.
Expected spy getCurrentUser to have been called.
Attempt 2:
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
beforeEach(function() {
spyOn(Auth, 'getCurrentUser');
});
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
afterEach(function() {
expect(Auth.getCurrentUser).toHaveBeenCalled();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
This doesn't work because Auth isn't available to be injected before my app module is loaded.
Error: [$injector:unpr] Unknown provider: AuthProvider <- Auth
Attempt 3:
As you can see, there's a chicken-egg problem here. I need to inject Auth and set up the spy before the module is loaded, but I can't because Auth isn't available to be injected before the module is loaded.
This blog posts mentions the chicken-egg problem and provides an interesting potential solution. The author proposes that I should create my Auth service manually using $provide before I load my module. Since I'm creating the service, not injecting it, I could do it before the module is loaded, and I could set up the spy. Then when the module is loaded, it'd use this created mock service.
Here is his example code:
describe('example', function () {
var loggingService;
beforeEach(function () {
module('example', function ($provide) {
$provide.value('loggingService', {
start: jasmine.createSpy()
});
});
inject(function (_loggingService_) {
loggingService = _loggingService_;
});
});
it('should start logging service', function() {
expect(loggingService.start).toHaveBeenCalled();
});
});
The problem with this, is that I need my Auth service! I only would want to use the mock one for the run block; I need my real Auth service elsewhere so I could test it.
I guess that I could create the actual Auth service using $provide, but that feels wrong.
Final question - for whatever code I end up using to deal with this run block problem, is there a way for me to extract it out so I don't have to re-write it for each of my spec files? The only way I could think to do it would be to use some sort of global function.
auth.factory.js
angular
.module('mean-starter')
.factory('Auth', Auth)
;
function Auth($http, $state, $window, $cookies, $q, $rootScope) {
return {
signup: function(user) {
return $http
.post('/users', user)
.then(function(response) {
angular.copy(response.data, $rootScope.user);
$cookies.put('userId', response.data._id);
$state.go('home');
})
;
},
login: function(user) {
return $http
.post('/login', user)
.then(function(response) {
angular.copy(response.data, $rootScope.user);
$cookies.put('userId', response.data._id);
$state.go('home');
})
;
},
logout: function() {
$http
.get('/logout')
.then(function() {
angular.copy({}, $rootScope.user);
$cookies.remove('userId');
$state.go('home');
})
.catch(function() {
console.log('Problem logging out.');
})
;
},
getCurrentUser: function() {
// user is logged in
if (Object.keys($rootScope.user).length > 0) {
return $q.when($rootScope.user);
}
// user is logged in, but page has been refreshed and $rootScope.user is lost
if ($cookies.get('userId')) {
return $http.get('/current-user')
.then(function(response) {
angular.copy(response.data, $rootScope.user);
return $rootScope.user;
})
;
}
// user isn't logged in
else {
return $q.when({});
}
}
};
}
Edit - failed attempt + successful attempt:
beforeEach(module('auth'));
beforeEach(inject(function(_Auth_) {
Auth = _Auth_;
spyOn(Auth, 'requestCurrentUser');
}));
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
// Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
// beforeEach(function() {
// spyOn(Auth, 'getCurrentUser');
// });
afterEach(function() {
expect(Auth.getCurrentUser).toHaveBeenCalled();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
I'm not sure why this wouldn't work (independent of the problem with using inject twice).
I was trying to get around having to use $provide as that initially felt hacky/weird to me. After thinking about it some more though, I now feel that $provide is fine, and that following your suggestion to use mock-auth is fantastic!!! Both worked for me.
In auth.factory.spec.js I just loaded the auth module (I'm calling it auth, not mean-auth) without loading mean-starter. This doesn't have the run block problem because that module doesn't have the run block code, but it allows me to test my Auth factory. Elsewhere, this works:
beforeEach(module('mean-starter', 'templates', function($provide) {
$provide.value('Auth', {
requestCurrentUser: jasmine.createSpy()
});
}));
As does the fantastic mock-auth solution:
auth.factory.mock.js
angular
.module('mock-auth', [])
.factory('Auth', Auth)
;
function Auth() {
return {
requestCurrentUser: jasmine.createSpy()
};
}
user.service.spec.js
beforeEach(module('mean-starter', 'mock-auth', 'templates'));
My understanding is that when you load your module in Angular unit
tests, the run block gets called.
Correct.
I'd think that if you're testing a component, you wouldn't want to
simultaneously be testing the run block, because unit tests are
supposed to just test one unit. Is that true?
Also correct, in that right now you are effectively testing the integration of Auth and your run block, and there is no isolation of one from the other.
If so, is there a way to prevent the run block from running? My
research leads me to think that the answer is "no", and that the run
block always runs when the module is loaded, but perhaps there's a way
to override this. If not, how would I test the run block?
As implemented, no you cannot prevent the run block from running. However, it remains possible with some minor refactoring as your question is ultimately one of modularisation. Without being able to see your module declaration, I would imagine it looks something like this:
angular.module('mean-starter', ['ngCookies'])
.factory('Auth', function($cookies) {
...
});
.run(function(Auth, $rootScope) {
...
});
This pattern can be broken into modules to support testability (and module reusability):
angular.module('mean-auth', ['ngCookies'])
.factory('Auth', function() {
...
});
angular.module('mean-starter', ['mean-auth'])
.run(function(Auth, $rootScope) {
...
});
This now allows you to test your Auth factory in isolation by loading the mean-auth module only into its test.
While this solves the problem of your run block interfering with your unit tests for Auth, you still face the problem of mocking Auth.getCurrentUser so as to test your run block in isolation. The blog post you referenced is correct in that you should be looking to leverage the configuration stage of the module to stub/spy on dependencies used during the run stage. Therefore, in your test:
module('mean-starter', function ($provide) {
$provide.value('Auth', {
getCurrentUser: jasmine.createSpy()
});
});
As to your final question, you can create reusable mocks by declaring them as modules. For example, if you wanted to create a reusable mock factory for Auth you define it in a separate file loaded prior to your unit tests:
angular.module('mock-auth', [])
.factory('Auth', function() {
return {
getCurrentUser: jasmine.createSpy()
};
});
and then load it in your tests subsequent to any module in which you require it, as angular will overwrite any service with the same name:
module('mean-starter', 'mock-auth');

Jasmine unit test AngularJS factory with two dependencies ($http and another factory returning promise)

I am using Ionic framework for custom applications. In the process, I am trying to write Unit test for the factory datastoreServices which has a dependency on DomainService and $http. I am kind a confused on the implementation of Jasmine Unit tests.
My factories are as follows.
app.factory("datastoreServices", ["$http", function($http) {
return {
getData: function(data, DomainService) {
return $http.post(DomainService.host + 'factor', data);
}
};
}]);
app.factory('DomainService', function() { //here
if (ionic.Platform.isAndroid()) {
return {
host: 'http://10.0.2.2:7001/'
}
}
return {
host: 'http://localhost:7001/'
}
})
And my unit test skeleton is as follows. It has two dependencies so, couldn't figure out how to proceed. This is what I got so far for in unit test file.
describe(
'datastoreServices',
function() {
beforeEach(module('Myapp'));
describe('getData'),
function() {
it("Should return correct values", inject(function(datastoreServices, DomainService, $httpBackend) {
expect(datastoreServices.getData(httpBackend.. /***something here!**/ )
.toEqual("2.2");
}))
}
I have very little knowledge on mocking and stuffs. Can someone help me test that factory datastoreServices. The following things are to be tested:
Is Http post making correct calls?
Is the function returning correct promise?
Here is the similar scenario of app in plnkr.
Idk, if I am asking too much. Thanks in advance.
The key principles are:
$http is mocked during testing, meaning that your server is not being actually called during your test execution
you must use $httpBackend in order to assert http calls and mock server response https://docs.angularjs.org/api/ngMock/service/$httpBackend
you can easily inject or mock any dependencies needed for your test
Here's an example based on your OP code:
describe('datastoreServices', function() {
beforeEach(module('MyApp'));
// get a reference to the $httpBackend mock and to the service to test, and create a mock for DomainService
var $httpBackend, datastoreServices, DomainService;
beforeEach(inject(function(_$httpBackend_, _datastoreServices_) {
$httpBackend = _$httpBackend_;
datastoreServices = _datastoreServices_;
DomainService = function() {
return {
host: 'http://localhost:7001/'
};
};
}));
// after each test, this ensure that every expected http calls have been realized and only them
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('calls http backend to get data', function() {
var data = {foo: 'bar'};
// write $http expectation and specify a mocked server response for the request
// see https://docs.angularjs.org/api/ngMock/service/$httpBackend
$httpBackend.expectPOST('http://localhost:7001/factor', data).respond(201, {bar: 'foo'});
var returnedData;
datastoreServices.getData(data, DomainService).success(function(result) {
// check that returned result contains
returnedData = result;
expect(returnedData).toEqual({bar: 'foo'});
});
// simulate server response
$httpBackend.flush();
// check that success handler has been called
expect(returnedData).toBeDefined();
});
});

Unit testing Angular with Breeze

I am trying to unit test angularjs with QUnit but get the error messages: $httpBackend.whenGET is not a function, $httpBackend.when is not a function. I have included angular mocks and angular breeze service (http://www.breezejs.com/documentation/breeze-angular-service) which uses the angular q library for promises and httpbackend instead of $.ajax for data transmission. I am still unable to mock any of the calls to the server. Some sample code:
var $httpBackend,
injector;
var SPAModule = angular.module("spa");
injector = angular.injector(['ng', 'spa']);
$httpBackend = injector.get("$httpBackend");
SPAModule.config(function ($provide) {
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
});
test("WHEN the controller is called THEN it should be created with the correct data on the scope", function () {
'use strict';
// Given
$httpBackend.whenGET("/Breeze/Data/Jobs").respond({ data: jobData });
$httpBackend.whenGET("/Breeze/Data/Metadata").respond({});
var routeParams = { id: "b" },
// When
controller = injector.get('$controller')(toriga.propertyController, {
$scope: theScope,
$window: windowMock,
$location: locationMock,
$routeParams: routeParams
}),
$rootScope = injector.get('$rootScope');
$httpBackend.flush();
$rootScope.$apply(); // forces results of promise to be executed
// Then
notEqual(controller, null, 'controller was created properly');
strictEqual(theScope.pageTitle, "Property", "pageTitle was set on the scope");
notEqual(theScope.job, null, "Job set on the scope");
ok(toastrMock.warning.notCalled, "No warning messages were displayed");
ok(toastrMock.error.notCalled, "No error messages were displayed");
});
This code used to work fine when I was not using breeze but now I have switched I can't seem to get it to work and the documentation is poor on how to get this working. Any help would be appreciated.
I can't tell all of the details of your tests. I can offer some comfort that it does work .. and pretty much as you'd expect.
Here is an extract from the test/specs/lookups.spec in the "Zza-Node-Mongo" sample (it's in github) in which I replay through the $httpBackend mock a (subset of) the server’s response to a Breeze client request for "lookup" reference entities.
I'm using Jasmine instead of QUnit but I hope you get the picture.
// simplified for presentation here but materially sufficient
describe("when lookups service receives valid lookups data", function () {
var $httpBackend, flush$q, lookups
var lookupsUrlRe = /breeze\/zza\/Lookups\?/; // RegEx of the lookups endpoint
beforeEach(module('app'));
beforeEach(inject(function(_$httpBackend_, $rootScope, _lookups_) {
$httpBackend = _$httpBackend_;
flush$q = function() { $rootScope.$apply(); };
lookups = _lookups_;
}));
beforeEach(function () {
$httpBackend.expectGET(lookupsUrlRe).respond(validLookupsResponse.data);
lookups.ready(); // THIS TRIGGERS CALL TO FETCHLOOKUPS
$httpBackend.flush();
});
it("doesn't bomb", function () {
expect(true).toBe(true);
});
it("'ready()' invokes success callback", function () {
var success = jasmine.createSpy('success');
lookups.ready(success);
flush$q(); // NOTE NEED TO FLUSH $Q IN THIS TEST
expect(success).toHaveBeenCalled();
})
it("has OrderStatus.Pending", function () {
expect(lookups.OrderStatus && lookups.OrderStatus.Pending).toBeDefined();
});
... more tests ...
});
The "lookups" service (app/services/lookups.js) calls breeze to fetch lookups data from the server.
function fetchLookups() {
return breeze.EntityQuery.from('Lookups')
.using(manager).execute()
.then(function () {
logger.info("Lookups loaded from server.");
extendService(manager)
})
.catch(function (error) {
error = util.filterHttpError(error);
logger.error(error.message, "lookups initialization failed");
throw error; // so downstream fail handlers hear it too
});
}
As you might imagine, this is a pretty deep integration test that starts with a service consumed by a ViewModel and goes all the way through the Breeze Angular Service through $http just about to the network boundary before being intercepted by $httpBackend.

Categories