Jest tests passed when in promises, but failed in async/await - javascript

I'm writing tests on my RESTful API using Jest + supertest.
My test.js looked like this at first:
const crypto = require('crypto')
const request = require('supertest')
const app = require('./app')
const genUUID = () => {
return ([1e7]+1e3+4e3+8e3+1e11).replace(/[018]/g, c =>
(c ^ crypto.randomFillSync(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
)
}
let uuid1 = genUUID()
let uuid2 = genUUID()
describe('Test /uuids', () => {
it('Get list of uuids', () => {
request(app).get('/api/uuids/').then(res =>
expect(res.statusCode).toBe(200)
)
})
})
describe('Test /uuids/:uuid', () => {
it('Get info of a not-existed product', () => {
request(app).get('/api/uuids/' + uuid1).then(res =>
expect(res.statusCode).toBe(400)
)
})
})
It works and all tests are passed.
But I love the style of async/await, so I switched the promises to async/await.
... // The previous part remains unchanged
describe('Test /uuids', () => {
it('Get list of uuids', async() => {
const res = await request(app).get('/api/uuids/')
expect(res.statusCode).toBe(200)
})
})
describe('Test /uuids/:uuid', () => {
it('Get info of a not-existed product', async () => {
const res = await request(app).get('/api/uuids/' + uuid1)
expect(res.statusCode).toBe(400)
})
})
This time, errors are raised.
console.error api/uuids.js:247
ERR!error: bind message supplies 1 parameters, but prepared statement "Get lists of UUIDs" requires 6
....
● Test /uuids/:uuid › Get info of a not-existed product
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Did I write the async/await correctly? Or was there any problems about async/await in Jest?
P.S. node version is v8.11.2

The promise version doesn't work actually. As said in comments (this and this), the test results are skipped, giving the false sense of passing the tests. Adding return to the promise results in the same errors as async/await case.
By mocking the app code with
const app = new (require('express'))()
app.get('/api/uuids', (req, res) => {
res.status(200).end()
})
app.get('/api/uuids/:uuid', (req, res) => {
res.status(200).end()
})
module.exports = app
The async/await one can tell there is sth wrong in GET /api/uuids/:uuid, while the promises one still report all tests are passed. So the async/await is correct instead.
And after some debug, it turns out that it's actually the problem of my code on querying database, so a postgres error was thrown in the first place.

Try to increase the timeout limit (by passing a number in miliseconds as second argument to it function):
describe('Test /uuids', () => {
it('Get list of uuids', async() => {
const res = await request(app).get('/api/uuids/')
expect(res.statusCode).toBe(200)
}, 30000)
})
describe('Test /uuids/:uuid', () => {
it('Get info of a not-existed product', async () => {
const res = await request(app).get('/api/uuids/' + uuid1)
expect(res.statusCode).toBe(400)
}, 30000)
})

Related

Change async/await to Promise

I would like to change the following async/await code
const mongoose = require('mongoose')
const supertest = require('supertest')
const app = require('../app')
const api = supertest(app)
test("total number of blogs", async () => {
const response = await api.get('/api/blogs')
expect(response.body).toHaveLength(1)
})
afterAll(() => {
mongoose.connection.close()
})
to a Promise like this:
const mongoose = require('mongoose')
const supertest = require('supertest')
const app = require('../app')
const api = supertest(app)
test("total number of blogs", () => {
api.get('/api/blogs')
.then( response => {
expect(response.body).toHaveLength(1)
})
})
afterAll(() => {
mongoose.connection.close()
})
I could not manage to solve it correctly and I keep getting an error message:
To clarify #jonrsharpe's comment you should return the promises from your test function:
test("total number of blogs", () => {
return api.get('/api/blogs')
.then( response => {
expect(response.body).toHaveLength(1)
})
})
In the first version you return a Promise in the test. The testing library receives this promise and waits for it to finish before seeing the test as "finished".
So what you'll want to do is:
test("total number of blogs", () => {
return api.get('/api/blogs')
.then( response => {
expect(response.body).toHaveLength(1)
})
})
Don't forget that the first example already works with promises (async/await is just promises)
Edited because I looked into the docs :p

Problem running Jest using CLS with async functions

I can't seem to get CLS to work with Jest.
The following code:
export {}
const { promises: fs } = require('fs')
describe('CLS tests', () => {
test('Can test CLS', async () => {
var createNamespace = require('cls-hooked').createNamespace
var session = createNamespace('session')
session.run(async function () {
await fs.readFile('package.json', 'utf-8')
console.log('I cant log this')
})
})
})
Results in the following error:
Cannot log after tests are done. Did you forget to wait for something
async in your test?
Attempted to log "I cant log this".
Why is it that my test appears to be exiting early?
Maybe you need to abstract out the asynchronous operations. I tried this on my system and it works.
const {promises: fs} = require('fs')
const runSession = () => new Promise((resolve, reject) => {
const createNamespace = require('cls-hooked').createNamespace
const session = createNamespace('session')
session.run(() => {
fs.readFile('package.json', 'utf-8')
.then(resolve)
})
})
describe('CLS tests', () => {
test('Can test CLS', async () => {
const result = await runSession()
console.log('hello')
console.log(result)
expect(result).toBeTruthy()
console.log('after expect...')
})
})
Good Luck...

Locked it method in chai

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');
} )
} );
} );

Jest beforeAll fetching from API done after describe executes so running tests for each fetched page fails

I want to create jest tests that fetches pages data (url, component data and all info about what is on what page) from API in beforeAll - and then loop over them and test them with pupeteer - but because I have to make tests that react to what is being fetched - I have to use describe. But it seems only it() or test() after beforeAll. Small, simplified example:
'use strict';
const helpers = require('./helpers');
describe('pages', () => {
let livePages;
beforeAll(async () => {
const { key } = await helpers.login();
const { pages } = await helpers.getPageList(key);
livePages = pages.filter(page => page.pageState === 'live');
});
test('smoke test', () => {
expect(livePages.length).not.toBe(0);
});
it('smoke test', () => {
expect(livePages.length).not.toBe(0);
});
describe('smoke test', () => {
livePages.forEach(page => {
test('locale test', () => {
expect(page.locale).toBe('de-DE');
});
});
});
});
and it fails with 'TypeError: Cannot read property 'forEach' of undefined' but both test() and it() passes (Tests: 1 failed, 2 passed, 3 total).
Is there any workaround that would allow me to run specific tests for each fetched page?

Best Way to Test Promises in Jest

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
})

Categories