I want to write tests for promise result and I dot'n want to resolve promise in each it/pit section.
I need smth like this:
describe('getData() results test', () => {
return getData().then(response => {
it('foo', () => expect(response.foo).toEqual(1));
it('bar', () => expect(response.bar).toEqual(2));
it('bar', () => expect(response.bar).toEqual(3));
});
});
If use beforeEach - promise will be resolved as many times as number of it sections. I need to resolve it once and then test response. There are a lot of test cases so I want to split all tests into it sections
The beforeAll function is called only once before all the specs in describe are run.
When return promise, Jest will wait for the promise to resolve before letting the test run.
describe('getData() results test', () => {
let data = null;
beforeAll(() => getData().then(response => {
data = response;
}));
it('foo', () => expect(data.foo).toEqual(1));
it('bar', () => expect(data.bar).toEqual(2));
it('bar', () => expect(data.bar).toEqual(3));
});
Have a look at the Async tutorial from the Jest documentation. I believe you need something like this:
describe('getData() results test', () => {
var response;
beforeEach(() => {
response = getData();
});
it('foo', () => { return response.then(r => expect(r.foo).toEqual(1))});
it('bar', () => { return response.then(r => expect(r.bar).toEqual(2))});
it('bar', () => { return response.then(r => expect(r.bar).toEqual(3))});
});
Key bit of the docs:
The promise that is being tested should be returned.
This is not an answer to your question but it may help people comming from Google
Jest will await the promises returned from beforeAll or beforeEach as #tuchk4 mentioned.
In my case, I forgot the it function and implemented the test right into describe. Surprisingly enough, this "works" but the promises from beforeAll and beforeEach will not be awaited and this makes all sense
I said "works" on quote because failed expect() will indeed fail but the Promise they return will not be awaited which will cause a UnhandledPromiseRejectionWarning.
You can simply do the following
describe('some test suite title', () => {
let response;
beforeAll(async (done) => {
// Do some Async. logic before running any test
await require('./somefile');
response = await getData();
done();
});
// Now run tests => response have the data
it('foo', () => expect(response.foo).toEqual(1));
it('bar', () => expect(response.bar).toEqual(2));
it('bar', () => expect(response.bar).toEqual(3));
});
Related
We have a method in our CLI which uses method returning a promise to print message to user.
exports.handler = (argv) => {
let customUtils = new Utils(argv);
Utils.deploy()
.then(res => console.log(`Ressource was deployed`))
.catch(e => {
console.error(`Ressource was not deployed`);
console.error(e);
process.exit(1);
});
}
We are looking for a way to test console errors and process exit in case of deploy() promise rejection.
We tried using sandbox stub then assert in an async test:
describe('when promise is errored', () => {
beforeEach(() => {
sandbox = sinon.createSandbox();
utilsStub = sandbox.stub(Utils.prototype, 'deploy').rejects('rejected');
processStub = sandbox.stub(process, 'exit');
consoleStub = sandbox.stub(console, 'error');
});
afterEach(() => {
sandbox.restore();
});
it('should call deploy and log the error before exiting', async () => {
await handler({});
expect(utilsStub).to.have.been.called;
expect(console.error).to.have.been.called;
});
});
This test doesn't work: AssertionError: expected error to have been called at least once, but it was never called.
The same happens when we expect(process.exit).to.have.been.called;. It's never called.
We successfuly tested the then part in a similary way:
describe('when promise is resolved', () => {
beforeEach(() => {
sandbox = sinon.createSandbox();
utilsStub = sandbox.stub(Utils.prototype, 'deploy').callsFake(() => Promise.resolve('some text'));
consoleStub = sandbox.stub(console, 'log');
});
afterEach(() => {
sandbox.restore();
});
it('should call deploy and print success message', async () => {
await handler({});
expect(utilsStub).to.have.been.called;
expect(console.log).to.have.been.calledWith('Ressource was deployed');
});
});
There are some things to fix the source and test file.
For source file, we must use customUtils to call deploy() function. Since, you can use async/await, convert it from Promise can produce better code.
exports.handler = async argv => { // put async
let customUtils = new Utils(argv);
try {
await customUtils.deploy(); // change to await and use customUtils
console.log(`Ressource was deployed`);
} catch (e) {
console.error(`Ressource was not deployed`);
console.error(e);
process.exit(1);
}
};
For test file, nothing changes
describe('when promise is errored', () => {
beforeEach(() => {
sandbox = sinon.createSandbox();
utilsStub = sandbox.stub(Utils.prototype, 'deploy').rejects('rejected');
processStub = sandbox.stub(process, 'exit');
consoleStub = sandbox.stub(console, 'error');
});
afterEach(() => {
sandbox.restore();
});
it('should call deploy and log the error before exiting', async () => {
await handler({});
expect(utilsStub).to.have.been.called;
expect(console.error).to.have.been.called;
expect(process.exit).to.have.been.called; // add it
});
});
UPDATED:
In case want to still use promise, we have to make sure we return the promise.
exports.handler = (argv) => {
let customUtils = new Utils(argv);
return customUtils.deploy() // <== specify return here
.then(res => console.log(`Ressource was deployed`))
.catch(e => {
console.error(`Ressource was not deployed`);
console.error(e);
process.exit(1);
});
};
Hope it helps
You need to be able await the result of exports.handler before you test your assertions. You are awaiting it, but exports.handler is not returning the promise, so there's nothing to await in the test — exports.handler returns undefined immediately so the test runs the assertions in the same event loop before console.error can be called.
I'm not sure why you aren't seeing similar problems in the test where the promise resolves. (Maybe worth checking that that test fails properly)
This should help:
exports.handler = (argv) => {
let customUtils = new Utils(argv);
//Utils.deploy() // <- is that a typo?
return customUtils.deploy()
.then(res => console.log(`Ressource was deployed`))
.catch(e => {
console.error(`Ressource was not deployed`);
console.error(e);
process.exit(1);
});
}
Also in your tests you are creating a spy with:
consoleStub = sandbox.stub(console, 'error');
But writing the assertion directly on console.error. I don't think this should work:
expect(console.error).to.have.been.called;
// maybe expect(consoleStub)...
With those changes the test passes for me and (more importantly) fails when I don't call console.error in the catch.
I have a js file which supplies some db operations. This file works with promises only which can be chained. To test that class I work with an async function.
The problem is, that whenever I work with promises inside my test function the it function gets blocked for every other test later.
Here are two examples:
'use strict'
const exec = require('child_process').exec
const path = require('path')
const request = require('request')
const expect = require('chai').expect
const createTableStatements = require('../data')
test()
async function test () {
await testGetUser()
console.log('1')
await testGetFaculties()
}
function testGetUser () {
return new Promise((resolve1) => {
describe('test get user', function () {
const db = require('../dbInterface')
it('test get user should be complete', function () {
db.dbFunctions.dropAll()
.then(onResolve => {
return db.dbFunctions.createTable(createTableStatements.createTableStatements.user)
}
)
.then(() => {
console.log('success create user table')
return db.dbFunctions.addUser('1', 'firstName', 'lastName', 'email')
})
.then(resolve => {
return db.dbFunctions.getUser('email', undefined)
})
.then(result => {
expect(result.toString().includes('dummy')).to.equal(false)
})
.then(resolve => {
return db.dbFunctions.dropAll()
})
.then(resolve => {
console.log('resolve')
resolve1()
})
.catch(err => console.log(err))
})
})
})
}
function testGetFaculties () {
return new Promise(resolve => {
describe('test get faculties', function () {
let db
before(function () {
db = require('../dbInterface')
})
console.log('displayed')
it('should work', function () {
console.log('locked')
expect(db.dbFunctions.getFaculties('hsa')).to.be.an('array').that.does.include('Science')
resolve()
})
})
})
}
And this is the output
resolve
1
displayed
As you can see console.log('locked') is not being processed.
What i figured out so far, that I only have this issue when I call expect within a then function. But this is necessary for my tests.
The test () function should contain much more tests, only for this question I shortened it.
And for clarification: If I only test methods type of testGetFaculties () which don't contains another promise chain inside it works like it should.
Any idea why this is like it is?
Most probably the console.log( 'locked' ); doesn't do anything, because your previous test case was not finished at all.
Writing describe, it, before inside a Promise and containing unreturned Promises is something that you should not do.
Much better test case would look like :
'use strict'
const exec = require('child_process').exec
const path = require('path')
const request = require('request')
const expect = require('chai').expect
const createTableStatements = require('../data')
// You use this in both test cases anyway
const db = require('../dbInterface');
describe('test get user', function () {
it('test get user should be complete', function () {
return db
// ^ returning promise will make sure that the test ends when the promise ends.
.dbFunctions
.dropAll()
.then(onResolve => { ... } )
...
)
} );
} );
describe('test get faculties', function () {
it('should work', function () {
return db
// ^ returning promise will make sure that the test ends when the promise ends.
.dbFunctions
.getFaculties('hsa')
.then( value => {
// ^ You actually need to test the value of the resolve promise
expect( value ).to.be.an('array').that.does.include('Science');
} )
} );
} );
I have a failing Jest test case for code where I'm using promises. It looks like the resolution of the promise is happening after the test has completed, meaning I can't check that my promise resolution code has been executed.
It feels like I need to make the event loop tick so the promise is resolved and the resolution code is executed, but haven't found anything that can do that in Jest.
Here's a sample case. The code to be tested:
const Client = require('SomeClient');
module.exports.init = () => {
Client.load().then(() => {
console.log('load resolved');
setTimeout(() => {
console.log('load setTimeout fired, retrying init');
module.exports.init();
}, 1000);
});
};
Test code:
jest.useFakeTimers();
const mockLoad = jest.fn().mockImplementation(() => Promise.resolve());
jest.mock('SomeClient', () => {
return {
load: mockLoad
};
}, { virtual: true });
const promiseTest = require('./PromiseTest');
describe('SomeClient Promise Test', () => {
it('retries init after 10 secs', () => {
promiseTest.init();
expect(mockLoad).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledTimes(1); // <-- FAILS - setTimeout has not been called
jest.runAllTimers();
expect(mockLoad).toHaveBeenCalledTimes(2);
});
});
The expect(setTimeout).toHaveBeenCalledTimes(1); assertion fails (setTimeout has not been called at all), I think because the promise has not yet been resolved.
Am I doing something wrong here? Can I cause the event loop to tick inside the test?
To tick the event loop inside your test, you should make it asynchronous.
A nice workaround was suggested on GitHub.
Having flushPromises as suggested there
function flushPromises() {
return new Promise(resolve => setImmediate(resolve));
}
your test will look like
describe('SomeClient Promise Test', () => {
it('retries init after 10 secs', () => {
promiseTest.init();
expect(mockLoad).toHaveBeenCalledTimes(1);
// notice return so jest knows that the test is asynchronous
return flushPromises()
.then(() => {
expect(setTimeout).toHaveBeenCalledTimes(1);
jest.runAllTimers();
expect(mockLoad).toHaveBeenCalledTimes(2);
});
});
});
Or the same using async/await:
describe('SomeClient Promise Test', () => {
it('retries init after 10 secs', async () => {
promiseTest.init();
expect(mockLoad).toHaveBeenCalledTimes(1);
await flushPromises();
expect(setTimeout).toHaveBeenCalledTimes(1);
jest.runAllTimers();
expect(mockLoad).toHaveBeenCalledTimes(2);
});
});
Not entirely sure why our React setup was different but Sergey's answer nearly got us there.
We needed these two bits in our test:
export function flushPromises(): Promise<void> {
return new Promise(jest.requireActual("timers").setImmediate);
}
await flushPromises();
Unless I'm misunderstanding something, the resolves and rejects (https://facebook.github.io/jest/docs/expect.html#resolves) won't be available until vNext. What is the recommended way now/in the meantime to test promises with Jest? Is it just putting expects in the thens and catches?
For example:
describe('Fetching', () => {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', () => {
MyService.fetch().catch(e => expect(e).toBeTruthy()); // see rejects/resolves in v20+
});
it('should return expected data', () => {
MyService.fetch(filters, null, api).then(serviceObjects => {
expect(serviceObjects).toHaveLength(2);
}).catch(e => console.log(e));
});
});
UPDATE 15 June 2019: Not too long after I posted this question, Jest started supporting this out of the box. I changed the accepted answer below to reflect the currently best way to do this.
UPDATE 8 Dec 2021: At some point Jest started supporting async/await. So while other methods noted work, I've taken to simply (for most cases) using something like:
it('should do something', async () => {
const expected = true;
expect(await funcToTest()).toEqual(expected);
});
As with most cases, async/await is much more readable than alternatives. The only case I use resolves or rejects now is for simple cases like:
it('should not throw when doing something', async () => {
await expect(funcToTest()).resolves.not.toThrow();
});
it('should throw when something is wrong', async () => {
await expect(funcToTest()).rejects.toThrow();
});
Nowadays you can write it in this way as well: docs
describe('Fetching', () => {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', () => {
expect.assertions(1);
return expect(MyService.fetch()).rejects.toEqual({
error: 'Your code message',
});
});
it('should return expected data', () => {
expect.assertions(1);
return expect(MyService.fetch(filters, null, api)).resolves.toEqual(extectedObjectFromApi);
});
});
Update (06.01.2019)
Agree that the accepted answer doesn't work correctly as line
expect.assertions(1); does all the magic. Link to docs
expect.assertions(number) verifies that a certain number of assertions
are called during a test. This is often useful when testing
asynchronous code, in order to make sure that assertions in a callback
actually got called.
So putting this line at the top will control that the specific number of assertions are made by the time when the test is run.
Either return a promise and expect in the resolve or catch
describe('Fetching', () = > {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', () = > {
return MyService.fetch()
.catch (e => expect(e).toBeTruthy()); // see rejects/resolves in v20+
});
it('should return expected data', () = > {
return MyService.fetch(filters, null, api)
.then(serviceObjects => {
expect(serviceObjects).toHaveLength(2);
})
});
});
or using async/await
describe('Fetching', () = > {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', async() = > {
try {
const r = await MyService.fetch()
} catch (e) {
expect(e).toBeTruthy()
}
});
it('should return expected data', async() = > {
const serviceObjects = await MyService.fetch(filters, null, api)
expect(serviceObjects).toHaveLength(2);
});
});
I was able to test JEST with AXIOS for HTTP REST calls like this.
it('has an API worth testing', async () => {
let httpResult = null;
await callThefunctionThatReturnsPromiseToMakeTheAxiosApiCall()
.then(function(result) {httpResult=result;})
.catch(function(err) {httpResult=err;});
expect(httpResult.data.myData).toBe("myExpectedValue");
});
or
it('has an API worth testing', async () => {
let httpResult = await callThefunctionThatReturnsPromiseToMakeTheAxiosApiCall();
expect(httpResult.data.myData).toBe("myExpectedValue");
});
For additional Jest matchers maintained by the Jest Community check out jest-extended.
https://jestjs.io/docs/expect
Using jest-extended you can expect your promise toResolve() or toReject(). Then you can expect the result or the error to match something. For example:
test('OK status', async () => {
const request = fetch(...)
await expect(request).toResolve() // First, make sure it resolves
const data = await request
expect(data).toEqual(...) // Then test the result
})
test('ERROR status', async () => {
const request = fetch(...)
await expect(request).toReject() // First, make sure it rejects
await request.catch((error) => expect(error).toBe('...')) // Then test the error
})
An output of my console. Note that the console logs are out of order (1,3,4,2 instead of 1,2,3,4)
Code here
it('can store file', () => {
console.log('1) file storage start')
return filestore.store.q(file).then(() => {
console.log('2) file storage done')
}).should.eventually.be.fullfilled
})
describe('block number', () => {
beforeEach(() => {
console.log('3) check blockNumber')
return web3.Q.all([
web3.eth.getBlockNumber.q().then((_blockNumber) => {
blockNumber = _blockNumber
}),
web3.eth.getMining.q().then((isMining) => {
})
])
})
it('can retreive files block number', () => {
console.log('4) retreive')
return filestore.getBlockNumber.q(fileHash).should.eventually.bignumber.equal(blockNumber)
})
})
This turned out to be a stupid typo. I typed fullfilled instead of fulfilled
I suspect you are getting a side-effect from Chai. Try testing without it first, for example:
const assert = require('assert');
it('can store file', () => {
console.log('1) file storage start')
return filestore.store.q(file).then(() => {
// Promise should have fulfilled. Nothing more to do.
// Using should and chai after this is probably causing the problem.
// But you should really add some sort of assertion here to
// be able to detect regressions.
console.log('2) file storage done')
});
});
describe('block number', () => {
let blockNumber;
beforeEach(() => {
console.log('3) check blockNumber')
return web3.Q.all([
web3.eth.getBlockNumber.q().then((_blockNumber) => {
blockNumber = _blockNumber
}),
web3.eth.getMining.q().then((isMining) => {
})
])
})
it('can retreive files block number', () => {
console.log('4) retreive')
return filestore.getBlockNumber.q(fileHash)
.then((result) => {
// I'm not sure if assert.equal will work with big numbers.
// You might need a different comparator here.
assert.equal(result, blockNumber, 'should equal the blocknumber));
});
});
});
Mocha knows how to handle a returned Promise so there's really no need for Chai. It's unnecessary sugar.