This question already has answers here:
Bluebird Each loop in Mocha not working
(2 answers)
Closed 6 years ago.
I'm new to JavaScript test frameworks. I would like to do a little optimization but I run into some problems. The project is using should.js
Here is the simplified version of my original test cases:
describe('Simple', function() {
describe('Test', function() {
it('should do something', function(done) {
somePromise.then(function(data) {
data.should.above(100);
done();
});
}
it('should do something else but alike', function(done) {
somePromise.then(function(data) {
data.should.above(100);
done();
});
}
}
});
I'm trying to do it this way:
var testFunc = function(data) {
it('should do something', function(done) {
data.should.above(100);
done();
});
}
describe('Simple', function() {
describe('Test', function() {
somePromise.then(function(data) {
testFunc(data);
});
somePromise.then(function(data) {
testFunc(data);
});
}
});
The promise is asynchronous, and maybe that's the reason why my "optimization" didn't work? I've found no "done" callback for describe function in docs.
Thanks in advance! Any help will be appreciate!
Your example does not work since Mocha has finished registering test cases when your promise resolves.
Testing the same promise with different assertions
To test a single promise using several assertions you simply need to create the promise at the start of the tests and then use it in the it blocks like the following:
describe('A module', function() {
var promise;
before(function () {
promise = createPromise();
});
it('should do something', function() {
return promise.then(function (value) {
value.should.be.above(100);
});
});
it('should do something else', function() {
return promise.then(function (value) {
value.should.be.below(200);
});
});
});
Note that if the promise is returned from an API call, the call will only be made once. The result is simply cached in the promise for the two test cases.
This also makes use of the fact that you can return promises from test cases instead of using a done callback. The test case will then fail if the promise is rejected or if any of the assertions in the then() calls fail.
Testing different promises with the same assertions
Assuming that you want to test different promises using the same assertions you could pass a function to testFunc that creates the promise to be tested.
var testFunc = function(promiseFactory) {
it('should do something', function(done) {
promiseFactory().then(function(data) {
data.should.above(100);
done();
});
});
}
describe('Simple', function() {
describe('Test', function() {
testFunc(function () { return createSomePromise(); });
testFunc(function () { return createSomeOtherPromise(); });
});
});
This works since Mocha's it function is run immediately inside the describe block. The promises are then created using the promiseFactory callback when the test cases are actually run.
As you can also return promises from test cases you can change testFunc to return an assertion as a promise like this:
var testFunc = function(promiseFactory) {
it('should do something', function() {
return promiseFactory().should.eventually.be.above(100);
});
}
Related
My unit test is not working hitting the then function for some reason. Here is the test code.
describe("Basic promise test", () => {
it("should trigger .then function", () => {
var mock = jasmine.createSpy('some method');
var promise = new Promise((resolve, reject)=> {
console.log("inside Promise");
resolve('something');
console.log("done!");
});
promise.then(mock);
promise.then(function () { //neither works!
mock();
console.log("resolved"); //code does reach here but only after test fails
});
expect(mock).toHaveBeenCalled();
});
});
I've tried using 'babel-polyfill', 'es6-promise' and 'promise-polyfill' to no avail. What am I doing wrong?
Jsfiddle for this: https://jsfiddle.net/L53zxe39/
The promise is resolved, but the then callback is only called in the next microtask, after the check expect(mock).toHaveBeenCalled(); has been made.
It is intended behaviour and designed to prevent ambiguity around promises. A .then callback is guaranteed to be called later, even if the promise is already resolved.
Asynchronous jasmine tests work in the following way:
describe("Basic promise test", () => {
it("should trigger .then function", (done) => {
var mock = jasmine.createSpy('some method');
var promise = new Promise((resolve, reject)=> {
console.log("inside Promise");
resolve('something');
console.log("done!");
});
promise.then(mock).then(() => {
expect(mock).toHaveBeenCalled();
done();
}).catch(e => {
done.fail(e);
});
});
});
You can use done.fail to explicitly fail the spec. This is needed to catch and notify jasmine about uncaught exceptions during tests.
I have a db.js set up to do all my database calls. This is an example of one of the functions that query the database.
db.getUserCount = function () {
return new Promise (function (resolve, reject) {
db.users.count().then(function (result) {
resolve (result);
}, function(e) {
reject (e);
});
});
};
I am pretty new to JavaScript and testing. I have used mocha and chai to test that it resolves like this:
describe('getUserCount', function() {
it('should be fulfilled when called', function() {
return db.getUserCount().should.be.fulfilled;
});
});
How can I test the reject part of the promises. Do I have to use something like sinon or is there some simple way to make the promise fail?
Make db.users.count() call to fail by either causing some change in database entry or in your api.
I ended up using sinon stubs so the other test would still pass.
describe('db.getUserCount rejection test', function() {
sinon.stub(db, 'getUserCount').returns(Q.reject(new Error(errorMessage)));
it('should be rejected when called', function() {
return db.getUserCount().should.be.rejected;
});
it.only('getUserCount responds with correct error message', function() {
return db.getUserCount().catch(function(err) {
expect(err.message).to.equal('Error could not connect to database');
});
});
});
I'm trying to test promises-chain sequence with Jest:
someChainPromisesMethod: function() {
async()
.then(async1)
.then(async2)
.then(result)
.catch(error);
}
While testing single promise is good documented not sure what is a proper way (not sure what to do TBO) to test this kind of chain. Let's assume all asyncs are mocked and just resolves promises (Promise.resolve) in theirs body.
So I need something that will test whole sequence.
You can use jest.fn() to mock implementation and check what the function has been called with and return what you want. You need to mock all async functions you have in your function and return what you want.
e.g.
async = jest.fn(() => {
return Promise.resolve('value');
});
async1 = jest.fn(() => {
return Promise.resolve('value1');
});
async2 = jest.fn(() => {
return Promise.resolve('Final Value');
});
You can use this in your test as
it('should your test scenario', (done) => {
someChainPromisesMethod()
.then(data => {
expect(async1).toBeCalledWith('value');
expect(async2).toBeCalledWith('value1');
expect(data).toEqual('Final Value');
done();
});
});
However, I would flatten your chain and test them separately if you have logic in them, that way you can test all possibilities easily.
Using done does not fix the issue, it will give you a false positive test. If for any reason an expectation fails, your test will timeout and you'll not have the real result.
The right solution is to return your Promise, so Jest will be able to evaluate the expect result correctly.
Following the #grgmo example, a better approach could be:
it('should your test scenario', () => {
return someChainPromisesMethod()
.then(data => {
expect(async1).toBeCalledWith('value');
expect(async2).toBeCalledWith('value1');
expect(data).toEqual('Final Value');
});
});
I have a fairly straightforward test that works against an Angular promise, which I'm resolving in the beforeEach function, but the then in my code is not ever firing and I can't see what I'm missing. These are written with TypeScript, but that doesn't really have any bearing on the problem.
Here is my test
describe('Refresh->', () => {
var controller = new Directives.Reporting.ReportDirectiveController($scope, $q, $location);
var called = false;
var defer: any;
beforeEach((done) => {
controller.drillReport = (drillReport: Models.drillReport): ng.IPromise<Models.drillData> => {
defer = $q.defer();
called = true;
defer.resolve({});
return defer.promise;
};
spyOn(controller, 'processResults');
controller.refresh();
done();
});
it('Calls DrillReport', () => {
expect(called).toBeTruthy();
});
it('Calls ProcessResults', () => {
expect(controller.processResults).toHaveBeenCalled();
});
});
The Refresh method in the controller looks like this:
refresh() {
this.drillReport({ drillReport: drillReport })
.then((results: Models.drillData) => {
parent.processResults(results, parent.availableDrills, this.columns, this.gridOptions, undefined, undefined);
});
}
What you are missing is that you will need access to use $scope, or $rootScope, so that you can call and force a digest cycle...
$scope.$digest();
The reason this is needed is that the resolved and rejected promises are processed during the digest loop. So while you are resolving the promise in your mock, the actual promise callback is not being invoked.
Following up on what #Brocco said, your code is calling done() before the promise is processed.
What you need it a way for your test code to know that that parent.processResults() has been called.
I suggest you have refresh return a promise that will be resolved just after parent.processResults(), and add controller.refresh().finally(() => { done(); }); in your test code.
I'm testing an angular.js app using karma with sinon-chai. I need to test a code like this:
var sync = function() {
async.then(function() {
// success
}, function() {
throw Error('foo');
});
}
sync is the function I want to test. async is a function returning a $q promise I want to test it fails throwing an error.
My ideal test suit:
describe('unit', function() {
it('should throw an error', function() {
expect(function() {
return sync();
}).to.throw(Error, 'foo');
})
});
But, it fails at afterEach() because the error is thrown on $digest.
How can I achieve this test?
Thank you very much!