I have the following jasmine test:
it('should resolve promise', inject(function ($q, $rootScope) {
function getPromise(){
var deferred = $q.defer();
setTimeout(function(){
deferred.resolve(true);
}, 1000);
return deferred.promise;
}
var p = getPromise();
var cb = jasmine.createSpy();
runs(function(){
expect(cb).not.toHaveBeenCalled();
p.then(cb);
$rootScope.$apply();
});
waitsFor(function(){
return cb.callCount == 1;
});
runs(function(){
expect(cb).toHaveBeenCalled();
$rootScope.$apply();
});
}));
I thought $rootScope.$apply was supposed to resolve all outstanding promises, but somehow it does not happen in this test.
How do i trigger promise resolving in a test like this? please help!
I think the $rootScope.$apply() is being called too soon in your case. This should work:
function getPromise(){
var deferred = $q.defer();
setTimeout(function(){
deferred.resolve(true);
$rootScope.$apply();
}, 1000);
return deferred.promise;
}
Update
You can inject mock $timeout service and resolve the promise in that explicitly using $timeout.flush().
it('should resolve promise', inject(function ($q, $timeout, $rootScope) {
function getPromise(){
var deferred = $q.defer();
$timeout(function(){
deferred.resolve(true);
}, 1000);
return deferred.promise;
}
// ...
$timeout.flush();
// ...
Related
I am trying to write a test for an action handler on one of my components. I am stubbing out the save method on one of my models to return a resolved promise using Em.RSVP.Promise.resolve()
in my component, i chain on that promise using then:
return target
.save()
.then(function(){
selected.rollback();
this.sendAction('quicklinkChanged', target);
}.bind(this),this.notify_user_of_persistence_error.bind(this, 'Save As'));
this is a pattern that i use a lot server-side where we use when for our promise library. however, when i do this client-side, i never end up inside the function in the then block so i cannot assert any of the functionality there in my unit tests.
can anyone provide any insight on the best way to do this?
We moved our callbacks out of the method so we could call them separately and verify functionality, or replace them and verify they were called.
Controller Example:
App.IndexController = Em.Controller.extend({
randomProperty: 1,
async: function(fail){
return new Em.RSVP.Promise(function(resolve, reject){
if(fail){
reject('fdas');
}else{
resolve('foo');
}
});
},
doAsyncThing: function(fail){
return this.async(fail).then(this.success.bind(this), this.failure.bind(this));
},
success: function(){
this.set('randomProperty', 2);
},
failure: function(){
this.set('randomProperty', -2);
}
});
Tests
test("async success", function(){
var ic = App.IndexController.createWithMixins();
stop();
ic.doAsyncThing(false).then(function(){
start();
equal(ic.get('randomProperty'), 2);
});
});
test("async fail", function(){
var ic = App.IndexController.createWithMixins();
stop();
ic.doAsyncThing(true).then(function(){
start();
equal(ic.get('randomProperty'), -2);
});
});
test("async success is called", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ic.success = function(){
ok(true);
};
stop();
ic.doAsyncThing(false).then(function(){
start();
});
});
test("async failure is called", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ic.failure = function(){
ok(true);
};
stop();
ic.doAsyncThing(true).then(function(){
start();
});
});
test("doAsyncThing returns a promise", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ok(ic.doAsyncThing(true).then);
});
Example: http://emberjs.jsbin.com/wipo/37/edit
I am using jasmine runner to test angular code.
describe('des1', function() {
var des1Var = function(){};
beforeEach() {
//....
}
describe('test1', function() {
var scope4Compile = $rootScope.$new();
var des2Var = des1Var(scope4Compile); // returns undefined.
beforeEach(function() {
des2Var = des1Var(scope4Compile); // returns des1Var() fine;
})
it('should do ', function(){
//should do...
})
it('should also do', function(){
//should also do...
})
})
})
I need to instantiate something once before the it statements, if run multiple times result is pretty bad. How can I get it done properly?
I believe it you call it once in the first beforeEach it will be run one time for each describe that is below it.
In the code below, des2Var will be set once for the whole test1 describe.
describe('des1', function() {
var des1Var = function () { };
beforeEach(function () {
var des2Var = des1Var();
});
describe('test1', function() {
it('should do ', function(){
//should do...
});
it('should also do', function(){
//should also do...
});
});
});
I'm running into trouble trying to mock my $http calls in Angular. When calling $httpBackend.flush() I'm getting the error: Error: No pending request to flush !
I've read up on many other posts of similar issues on here, but I still can't seem to get it to work by adding $rootScope.$digest() or adding $httpBackend.expect
I'm testing a service function:
getAll: function (success, error) {
$http.get(apiRootService.getAPIRootHTTP() + "/items")
.success(success).error(error);
},
Using this .spec:
describe ('itemAPI Module', function (){
var $httpBackend, $rootScope, itemAPI, apiRootService;
var projectID = 123;
beforeEach(module('itemAPI'));
beforeEach(module('mApp'));
/**
* Set up variables pre-insertion
*/
beforeEach( inject( function(_$rootScope_, _$httpBackend_, _apiRootService_){
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
apiRootService = _apiRootService_;
}));
describe('itemAPI factory', function (){
it('can get an instance of the metaAPI factory', inject(function(_itemAPI_){
itemAPI = _itemAPI_;
expect(metaAPI).toBeDefined();
}));
describe("get all items", function (){
it("will return an error", function (){
var url = apiRootService.getAPIRootHTTP() + "/items";
$httpBackend.whenGET(url)
.respond(400);
var error;
itemAPI.getAll(
function(data, status){
//this is success
},
function(data, status){
error = true;
});
$httpBackend.expectGET(url);
$rootScope.$digest();
$httpBackend.flush();
expect(error).toBeTruthy();
});
});
});
});
Question: How do I fake my pointFactory so I can Jasmine Unit Test it.
I have the Following Directive.
It takes the html sends it to a factory and the uses the response for some logic
CommonDirectives.directive('TextEnrichment',['PointFactory','appSettings', function (pointFactory,settings) {
return {
restrict: 'A',
link : function (scope, element, attrs) {
var text = element.html();
pointFactory.getPoints(text).then(function(response){
})}}}]);
So far my unit tests looks like this, however it doesn't work since I'm not injecting the factory.
beforeEach(module('app.common.directives'));
beforeEach(function () {
fakeFactory = {
getPoints: function () {
deferred = q.defer();
deferred.resolve({data:
[{"Text":"Some text"}]
});
return deferred.promise;
}
};
getPointsSpy = spyOn(fakeFactory, 'getPoints')
getPointsSpy.andCallThrough();
});
beforeEach(inject(function(_$compile_, _$rootScope_,_$controller_){
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Factory to have been Called', function () {
var element = $compile('<div data-text-enrichment=""> Text </div>')($rootScope)
expect(getPointsSpy.callCount).toBe('1');
});
Update
Following advice from Felipe Skinner I have updated the test with the following
beforeEach(function(){
module(function($provide){
$provide.factory('PointFactory',getPointsSpy)
})
});
However I get the following error:
TypeError: 'undefined' is not a function (evaluating
'pointFactory.getPoints(text)')
You can use the $provide to inject your controller dependencies.
Here's my beforeEach for example:
describe('MyCtrl', function() {
var $controller,
$scope,
$httpBackend,
windowMock,
registerHtmlServiceMock,
mixPanelServiceMock,
toastMock;
beforeEach(function() {
windowMock = { navigator: {} };
registerHtmlServiceMock = {};
mixPanelServiceMock = jasmine.createSpyObj('mixpanel', ['track']);
toastMock = jasmine.createSpyObj('toast', ['error']);
module('myModule');
module(function($provide) {
$provide.value('$window', windowMock);
$provide.value('RegisterHtmlService', registerHtmlServiceMock);
$provide.value('MixPanelService', mixPanelServiceMock);
$provide.value('ToastService', toastMock);
});
inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
$scope = _$rootScope_.$new();
$controller = _$controller_('CourseSelectionCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
});
});
// my test cases
});
I haven't tried mocking a function that returns some value. Those two mocks (mixpanel-track and toast-error) are for "void" functions.
UPDATE:
Try changing the previous $provide with this type of injection then.
Change from this:
module(function($provide) {
$provide.value('$window', windowMock);
$provide.value('RegisterHtmlService', registerHtmlServiceMock);
$provide.value('MixPanelService', mixPanelServiceMock);
});
inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
$scope = _$rootScope_.$new();
$controller = _$controller_('CourseSelectionCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
});
To this:
beforeEach(inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
mixPanelService = mixPanelServiceMock;
$scope = _$rootScope_.$new();
$controller = _$controller_('MyCtrl', { $scope: $scope, MixPanelService: mixPanelService });
$httpBackend = _$httpBackend_;
}));
The rest of the code should be the same, except for that. Let me know if this works
I'm trying to figure out where to put a function to delete database and close connection after all tests have run.
Here are my nested tests:
//db.connection.db.dropDatabase();
//db.connection.close();
describe('User', function(){
beforeEach(function(done){
});
after(function(done){
});
describe('#save()', function(){
beforeEach(function(done){
});
it('should have username property', function(done){
user.save(function(err, user){
done();
});
});
// now try a negative test
it('should not save if username is not present', function(done){
user.save(function(err, user){
done();
});
});
});
describe('#find()', function(){
beforeEach(function(done){
user.save(function(err, user){
done();
});
});
it('should find user by email', function(done){
User.findOne({email: fakeUser.email}, function(err, user){
done();
});
});
it('should find user by username', function(done){
User.findOne({username: fakeUser.username}, function(err, user){
done();
});
});
});
});
Nothing seems to work. I get Error: timeout of 2000ms exceeded
You can define a "root" after() hook before the 1st describe() to handle cleanup:
after(function (done) {
db.connection.db.dropDatabase(function () {
db.connection.close(function () {
done();
});
});
});
describe('User', ...);
Though, the error you're getting may be from the 3 asynchronous hooks that aren't informing Mocha to continue. These need to either call done() or skip the argument so they can be treated as synchronous:
describe('User', function(){
beforeEach(function(done){ // arg = asynchronous
done();
});
after(function(done){
done()
});
describe('#save()', function(){
beforeEach(function(){ // no arg = synchronous
});
// ...
});
});
From the docs:
By adding a callback (usually named done) to it() Mocha will know that it should wait for completion.
I implemented it a bit different.
I removed all documents in the "before" hook - found it a lot faster than dropDatabase().
I used Promise.all() to make sure all documents were removed before exiting the hook.
beforeEach(function (done) {
function clearDB() {
var promises = [
Model1.remove().exec(),
Model2.remove().exec(),
Model3.remove().exec()
];
Promise.all(promises)
.then(function () {
done();
})
}
if (mongoose.connection.readyState === 0) {
mongoose.connect(config.dbUrl, function (err) {
if (err) {
throw err;
}
return clearDB();
});
} else {
return clearDB();
}
});