Jest how to test express API POST request? - node.js

I need to test if my POST request to my endpoint works properly with a Jest test. I had the idea of first getting the count of my Services table (I'm using sequelize orm), then to send a new post request and to finally get the new count and compare if the old count + 1 will equal to the new count, if true then the POST request works just fine.
test('Create a valid Service', async (done) => {
const service = {
name: "cool",
description: "description"
};
await Service.count().then(async function (count) {
await request(app)
.post('/api/services')
.send(service)
.then(async () => {
await Service.count().then(function (newcount) {
expect(newcount).toBe(count + 1);
});
})
.catch(err => console.log(`Error ${err}`));
});
});
For me the test looks fine, but when I run it I get:
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Is something missing or is there even a better way to test a POST request? with Jest?

It is because you are not calling the done callback passed in jest callback function. It can be done like this.
test('Create a valid Service', async(done) => {
const service = {
name: "cool",
description: "description"
};
await Service.count().then(async function (count) {
await request(app)
.post('/api/services')
.send(service)
.then(async() => {
await Service.count().then(function (newcount) {
expect(newcount).toBe(count + 1);
// execute done callback here
done();
});
})
.catch(err => {
// write test for failure here
console.log(`Error ${err}`)
done()
});
});
});
You can also write this code in this way so that the readability can be improved and maximize the use of async/await.
test('Create a valid Service', async(done) => {
const service = {
name: "cool",
description: "description"
};
try {
const count = await Service.count();
await request(app).post('/api/services').send(service)
const newCount = await Service.count()
expect(newCount).toBe(count + 1);
done()
} catch (err) {
// write test for failure here
console.log(`Error ${err}`)
done()
}
});
By default Jest also resolves the promise in async/await case. We can achieve this without the callback function also
test('Create a valid Service', async() => {
const service = {
name: "cool",
description: "description"
};
try {
const count = await Service.count();
await request(app).post('/api/services').send(service)
const newCount = await Service.count()
expect(newCount).toBe(count + 1);
} catch (err) {
// write test for failure here
console.log(`Error ${err}`)
}
});

Related

Postgres, Node, Jest - Splitting testes into two suites results in deadlock

I have two suites of tests, one for Accounts and one for Transactions, accounts.spec.js and transactions.spec.js. I'm also using supertest.
When I run them in jest, I get the error error: deadlock detected.
But if I move all describe blocks to one single spec.js file, and run only that file in jest, I get no errors.
The Account router is:
accountRouter.js
router.get('/accounts', async (req, res) => {
const result = await pool.query('SELECT * FROM accounts')
if (result.rows.length > 0) {
return res.status(200).send({ accounts: result.rows })
} else {
return res.status(404).send({ msg: 'No Account Found' })
}
})
And the Account test file is:
accounts.spec.js
describe('Accounts', () => {
beforeAll(() => {
return pool.query('BEGIN')
})
afterAll(() => {
return pool.query('ROLLBACK')
})
afterEach(() => {
return pool.query('TRUNCATE accounts')
})
it('returns 200 and all accounts', async () => {
await request(app).post(`${apiURL}/accounts`).send({title: 'Investments',})
const response = await request(app).get(`${apiURL}/accounts`).send()
expect(response.status).toBe(200)
expect(response.body.accounts.length).toBe(1)
});
})
The Transactions router is:
transactionsRouter.js
router.get('/:id/deposit', async (req, res) => {
const id = req.params.id
const result = await pool.query('SELECT * FROM accounts WHERE acc_id = ($1)', [id])
if (result.rows.length > 0) {
return res.status(200).send({destinationAccount: result.rows[0]})
} else {
return res.status(404).send({validationErrors: {invalidId: 'Account Not Found'}})
})
And the Transactions test file is:
transactions.spec.js
describe('Transactions DEPOSIT', () => {
beforeAll(() => {
return pool.query('BEGIN')
})
afterAll(() => {
return pool.query('ROLLBACK')
})
afterEach(() => {
return pool.query('TRUNCATE accounts')
})
afterEach(() => {
return pool.query('TRUNCATE transactions')
})
it('DEPOSIT returns 200 OK and account by id', async () => {
const insertMockAccountQuery = 'INSERT INTO accounts (title) VALUES ($1) RETURNING acc_id, title, budget';
const mockAccount = await pool.query(insertMockAccountQuery, [title])
const existingAccount = mockAccount.rows[0];
const response = await request(app).get(`${apiURL}/transactions/${existingAccount.acc_id}/deposit`).send();
expect(response.status).toBe(200);
expect(response.body.destinationAccount).toEqual(existingAccount);
})
});
Can anyone help me figure out the problem, please?
Thanks
Jest runs tests from one describe sequentially. Whereas runs tests from multiple files simultaneously.
In order to run all the tests sequentially the CLI flag --runInBand could be used.
More details here:
Are tests inside one file run in parallel in Jest?

Cloud Function finished with status: 'timeout' while adding data to Firestore

Here's the code,
exports.onEmailRecieved = functions.database
.ref("/emails/recieved/{id}/")
.onCreate(async (snapshot, context) => {
const email = snapshot.val();
const id = context.params.id;
const trimmedEmailBody = String(email.body).replace("\n", "").trim();
if (trimmedEmailBody === cmd) {
const queueRef = fs.collection("requests").doc("all").collection("queue");
await fs
.runTransaction(async (t) => {
const doc = await t.get(queueRef);
const size = doc.size;
console.log(`Size: ${size}`);
console.log("Adding to queue.");
await queueRef
.add({
email: email.email,
subject: email.subject,
body: email.body,
})
.then(() => {
console.log("Successfully added to queue.");
})
.catch((err) => {
console.log(err);
})
.finally(() => {
console.log("It's finally over.");
});
return console.log("Worked?");
})
.then(() => {
return console.log("Complete");
})
.catch((err) => {
return console.log(err);
});
return console.log("Worked I guess.");
} else {
return console.log("Not equal.");
}
});
Don't mind the bunch of useless console.logs. Added em to debug the error.
That first console.log gets called and then nothing, no then, catch or finally functions get triggered and I get a function finished with status: 'timeout' message in the logs.
What I'm doing wrong?
The add() method returns a promise which then when you await a promise, the function is paused in a non-blocking way until the promise settles. It will wait for the transaction to be finished before resolving the creation of the document which results in timeout of the cloud function which by default is 1min. By removing the await on the add method you're instead executing the function. See code below:
messageRef
.add({
email: "email",
subject: "subj",
body: "body",
})
.then(() => {
console.log("Successfully added to queue.");
})
.catch((err) => {
console.log(err);
})
.finally(() => {
console.log("It's finally over.");
});
This will now return something like this:
Size: 1
Adding to queue.
test
Successfully added to queue.
It's finally over.
For more relevant information, you may check this documentations:
Sync, async, and promises
CollectionReference
How to use promises

multiple async-await get requests not working in PERN stack app

I'm working on building an inventory management application using PERN stack. I have a modal where I need to make 2 GET requests and when I console.log in front end both requests are getting Status 200 response. However in my express server, first get request is working fine but the second request is not receiving anything.
My frontend code
const openModal = async () => {
setDetailModalOpen(true)
try {
await Promise.all([
(async () => {
const serial_number = props.bacsSerial
const response = await fetch(`http://localhost:5000/bacslist/demoinventory/${serial_number}`)
const parseResponse = await response.json()
console.log(response)
setInputs({
bacsUnit: parseResponse.bacs_unit,
serialNumber: parseResponse.serial_number,
partNumber: parseResponse.part_number,
bacsLocation: parseResponse.bacs_location,
description: parseResponse.bacs_description
})
setBacsId(parseResponse.id)
setBacsData(parseResponse)
})(),
(async () => {
const response2 = await fetch(`http://localhost:5000/bacslist/demoinventory/${bacsId}`)
console.log(response2)
})()
])
} catch (err) {
console.error(err.message)
}
}
My backend code
router.get("/demoinventory/:serial_number", async (req, res) => {
console.log('This one is working')
try {
const {serial_number} = req.params
const getDemoBacs = await pool.query(
"SELECT * FROM demo_inventory WHERE serial_number = $1", [serial_number]
)
res.json(getDemoBacs.rows[0])
} catch (err) {
console.error(err.message)
}
})
router.get("/demoinventory/:bacsId", async (req, res) => {
console.log(req.params)
console.log('This one is not working')
try {
const getHistoryData = await pool.query(
"SELECT * FROM demo_inventory_history"
)
console.log(getHistoryData)
res.json(getHistoryData)
} catch (err) {
console.error(err.message)
}
})
Sorry, Kinda new to this stuff so this isn't exactly an answer but I'm not allowed to leave a comment. I can't see your state variables with the code you posted, but are you sure that BacsId is being set to state before it is used in the second call, or is the parameter in the second call being sent empty, thus not using the right URL? Just a thought.

Mocking in jest and express

I have some code in an Express route which talks to AWS Cognito and am having trouble working out how to mock it in tests.
cognitoExpress.validate(accessTokenFromClient, (err, response) => {
if (err) return res.status(401).json({ error: err });
res.json({ data: `Hello ${response.username}!` });
});
Then in my test I want to say cognitoExpress.validate should be called once and return {username: 'test user'} so that it doesnt hit the network and doesnt actually call AWS Cognito
it('It should returns 200 with a valid token', async done => {
const { cognitoExpress } = require('../helpers/cognitoExpress');
// I have tried
jest.mock('../helpers/cognitoExpress');
// and this
jest.mock('../helpers/cognitoExpress', () => ({
validate: jest.fn()
}));
const token = 'sfsfdsfsdfsd';
const response = await request.get('/').set('Authorization', token);
expect(cognitoExpress.validate).toHaveBeenCalledWith(token);
expect(response.body).toEqual({ data: 'Hello test user' });
done();
});
Thanks in advance....
let spyInstance = undefined;
beforeAll(() => {
spyInstance = jest.spyOn(cognitoExpress.prototype, "validate").mockImplementation(() => {
// Replace the body of 'validate' here, ensure it sets
// response body to {username: 'test user'} without calling AWS
...
});
});
afterAll(() => {
expect(spyInstance).toBeDefined();
expect(spyInstance).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
});
it("It should call mocked cognitoExpress.validate once", async done => {
...
});
A similar and working test in my project. Instead of cognitoExpress.validate it mocks and tests SampleModel.getData
Create file ../helpers/__mocks__/cognitoExpress.js with mocked function you want to use. It is essential to call the folder __mocks__. You can modify a functions and return any data you want.
example
module.exports = {
validate: () => { username: 'test user' }
}
Now you can use jest.mock('../helpers/cognitoExpress'), but I recommend you to place it to some global or test setup file, not to separate tests.
Jest Manual Mocks

How can I unit test async database calls without changing the route?

So I have a route with a database call in a separate file:
module.exports = (req, res) => {
const sql = `SELECT Topic FROM surveys WHERE ID="${req.params.id}"`;
model.connection.query(sql, (err, result) => {
if (err) res.status(500).send(err);
res.send(result);
});
};
In my test file, I create fake req and res objects to test different inputs:
const req = {
body: {},
params: {
id: '',
}
};
const res = {
message: '',
stat: 200,
send(arg) {
this.message = arg;
},
status(number) {
this.stat = number;
return this;
}
};
And call it in every test:
it('Should return status 200 if parameter is a number string', () => {
req.params.id = '1';
getTopic(req, res);
chai.expect(res.stat).to.equal(200);
});
My question is, how can I test it asynchronously, without affecting my route?
I've seen Mocha documentation explaining this, but they require a callback in a function.
it('Should return status 200 if parameter is a number string', (done) => {
req.params.id = '1';
getTopic(req, res); // Cannot give any callback
chai.expect(res.stat).to.equal(200);
done(); //Doesn't work
});
I also tried using async/await, but it didn't work (I lack knowledge of how to use it, maybe that's why)
it('Should return status 200 if parameter is a number string', async () => {
req.params.id = '1';
await getTopic(req, res);
chai.expect(res.stat).to.equal(200);
});
And I've seen this question, and didn't help at all: unit testing express route with async callback

Resources