How to end async mocha test inside a then() - mocha.js

I need to perform two consecutive POSTs in my test, and so I have the second nested in a callback inside of a then(). When I try running my test, I get this error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Here is my code:
it('it should not create a user with a username already in the database', (done) => {
let user = {
"username": "myusername",
"password": "password"
};
chai.request(server)
.post('/user')
.send(user)
.then((response) => {
chai.request(server)
.post('/user')
.send(user)
.then((res) => {
res.should.have.status(406);
done();
});
});
});
I've tried increased my timeout limit, and that didn't work. What am I missing here?

You should always have a .catch.
.then(response => ...)
.catch(e => assert.equal(e.message, "Expecting data"))

Mocha supports promises, and it's best to use that support if you're testing promise-based code:
it('it should not create a user with a username already in the database', () => {
let user = {
"username": "myusername",
"password": "password"
};
return chai.request(server)
.post('/user')
.send(user)
.then((response) => {
return chai.request(server)
.post('/user')
.send(user)
.then(() => {
// We're _expecting_ a promise rejection, so throw if it resolves:
throw Error('should not be reached');
}, res => {
res.should.have.status(406);
});
});
});
Otherwise, the following may happen: the assertion (res.should.have.status(406)) may fail, which throws an error, which causes done never to get called, causing a timeout.
You can catch that assertion error with .catch and call done with the error, but you have to do that for all your tests, which is a bit tedious. It's also error-prone, because if, for some reason, a new error gets thrown inside the .catch, you end up with the same problem.

Ok, I figured it out. Found the answer on this post: When testing async functions with Mocha/Chai, a failure to match an expectation always results in a timeout
The reason it was never hitting my done calls was that it was hitting an uncaught exception and never returning. I wasn't providing a failure callback inside of my thens, and the Not Acceptable exception was being thrown.
Here is my fixed code:
it('it should not create a user with a username already in the database', (done) => {
let user = {
"username": "anh",
"password": "pass"
};
chai.request(server)
.post('/user')
.send(user)
.then((response) => {
chai.request(server)
.post('/user')
.send(user)
.then(() => {
throw Error('Something went wrong');
},
(res) => {
res.should.have.status(406);
done();
});
},
(res) => {
throw Error('Something went wrong');
});
});

Related

How to fail cypress test from inside the Promise.prototype.catch() block?

I'm using a node library to execute api calls for test data setup and teardown. The library works as follows:
someApiServiceObject
.executeApiCall({... parameters})
.then(doSomethingWithResults())
.catch(() => {
// Here I would like to fail the test as something has gone wrong
})
If the request fails for some reason, I only learn about it by the Promise returning from executeApiCall function being rejected - hence the catch block.
But if I put throw new Error(); into the catch block or remove the catch block, I can see the (uncaught exception) Error: in the cypress console, but the test still passes.
Can someone advise me on how this case should be handled correctly?
The test:
it('List projects', () => {
projectsApi.projectsList({})
.then(() => {
cy.log('Success');
}).catch(() => {
throw new Error();
});
});
If you call someApiServiceObject.executeApiCall({...parameters}) in a task (since it's a node library), you should just be able to return the promise and Cypress handles failing the test. Don't catch() within the task.
module.exports = (on, config) => {
on('task', {
api(parameters) {
return someApiServiceObject
.executeApiCall({... parameters})
.then(doSomethingWithResults())
},
})
}
If that fails, follow this pattern Return number of files in the folder
module.exports = (on, config) => {
on('task', {
countFiles(folderName) {
return new Promise((resolve, reject) => {
someApiServiceObject
.executeApiCall({... parameters})
.then(doSomethingWithResults())
.then((results) => resolve(results))
.catch((err) => reject(err))
})
})
},
})
}
From comments, I think there's a assumption being made that .executeApiCall() must be returning a promise, but that may not be the case.
For example cy.get(...) has a .then(...) method, but it does not return a promise, it just has a .then() method.
If .executeApiCall() does actually return a promise, the first example is all you need. If it does not, you need to wrap the code.
Cypress will recognise a promise returned from a task, and use resolve or reject accordingly.

Apollo Server Health Check custom response

I am currently looking to provide more information to the health check other than status: pass. Is this possible? I tried sending test strings unfortunately, I am still seeing the same response json. Thank you in advance!
Code:
onHealthCheck: (req) => {
return new Promise((resolve, reject) => {
if(true) {
console.log(req);
resolve(req);
//resolve("test")
} else {
reject();
}
})
}
onHealthCheck implementation looks like this (express is used):
if (onHealthCheck) {
onHealthCheck(req)
.then(() => {
res.json({ status: 'pass' });
})
.catch(() => {
res.status(503).json({ status: 'fail' });
});
}
so as you can see, it returns hardcoded value.
You can always implement your custom health check also using express.

Cypress sharing variables/alias between Hooks?

So I have a pretty `before` and `beforeEach` function that runs before all tests. It looks something like this:
describe("JWT Authentication", function() {
before(function() {
// custom command runs once to get token for JWT auth
// alias token as 'user' for further use
cy.get_auth_token().as('user')
})
beforeEach(function() {
// before each page load, set the JWT to the aliased 'user' token
cy.visit("/", {
onBeforeLoad(win) {
// set the user object in local storage
win.localStorage.setItem("token", this.user.token);
}
})
})
it("a single test...", function() {
//do stuff
});
The custom command is also pretty simple:
Cypress.Commands.add("get_auth_token", () => {
cy.request("POST", Cypress.env("auth_url"), {
username: Cypress.env("auth_username"),
password: Cypress.env("auth_password")
})
.its("body")
.then(res => {
return res;
});
})
The custom command itself works and retrieves the token as expected. However when it comes to the beforeEach it has no idea what this.user.token is. Specifically not knowing what user is.
One option is of course calling the command in every beforeEach which is what the JWT Cypress recipe/example spec does. However this feels excessive because in my case I do not NEED to grab the token every test. I only need to grab it once for this set of tests.
So how can I share the token to the beforeEach hook with a Cypress custom command.
I ran a few tests, all the bits seem to work!
The following does not give you an answer, but may help you debug.
Passing token between before() and beforeEach()
Assume we have a user in before(), does it get to the onBeforeLoad() callback?
describe("JWT Authentication", function() {
before(function() {
const mockUser = { token: 'xyz' };
cy.wrap(mockUser).as('user');
})
beforeEach(function() {
cy.visit("http://example.com", {
onBeforeLoad(win) {
console.log(this.user.title); // prints 'xyz'
}
})
})
it("a single test...", function() {
//do stuff
})
});
Is the custom command working
I can't find a generic mock for an Auth check, but any cy.request() that gets an object should be equivalent.
I'm hitting typicode.com and looking for the title property
describe("JWT Authentication", function() {
Cypress.Commands.add("get_auth_token", () => {
cy.request("GET", 'https://jsonplaceholder.typicode.com/todos/1')
.its("body")
.then(body => {
console.log('body', body); // prints {userId: 1, id: 1, title: "delectus aut autem", completed: false}
return body;
});
})
before(function() {
cy.get_auth_token()
.then(user => console.log('user', user)) // prints {userId: 1, id: 1, title: "delectus aut autem", completed: false}
.as('user')
})
beforeEach(function() {
cy.visit("http://example.com", {
onBeforeLoad(win) {
console.log(this.user.title); // prints 'delectus aut autem'
}
})
})
it("a single test...", function() {
//do stuff
})
});
Custom command
This shorter version also seems to work
Cypress.Commands.add("get_auth_token", () => {
cy.request("GET", 'https://jsonplaceholder.typicode.com/todos/1')
.its("body");
})

Mocha Chai Sequelize: I can't make tests fail

I am trying to write test for a model in sequelize, but I do not understand why it is not failing
it('should find user by id', (done) => {
users.findByPk(2)
.then((retrievedUser) => {
expect(retrievedUser.dataValues).to.deep.equal('it should break');
done();
})
.catch((err) => {
console.log(`something went wrong [should find user by id] ${err}`);
done();
})
});
When I run the test the output is the following
something went wrong [should find user by id] AssertionError: expected { Object (id, email, ...) } to deeply equal 'it should break'
1 -__,------,
0 -__| /\_/\
0 -_~|_( ^ .^)
-_ "" ""
1 passing (40ms)
If someone want to watch the full code, I created a project
For an asynchronous Mocha test to fail, pass an error as an argument to the done callback function
it('should find user by id', (done) => {
users.findByPk(2)
.then((retrievedUser) => {
expect(retrievedUser.dataValues).to.deep.equal('it should break');
done();
})
.catch((err) => {
console.log(`something went wrong [should find user by id] ${err}`);
done(err);
})
});
Alternatively, use an async function without a callback:
it('should find user by id', async () => {
const retrievedUser = await users.findByPk(2);
try {
expect(retrievedUser.dataValues).to.deep.equal('it should break');
} catch (err) {
console.log(`something went wrong [should find user by id] ${err}`);
throw err;
}
});
That said, I wouldn't recommend logging the error message of failing tests, because that's what Mocha already does for you in a typical setup. So I would get rid of the try-catch block in the example above.
it('should find user by id', async () => {
const retrievedUser = await users.findByPk(2);
expect(retrievedUser.dataValues).to.deep.equal('it should break');
});

How to get rid of timeout error in mocha-chai test despite using done() for async call?

I am using setTimeout in mocha test suite to insert a 20 seconds delay before making the last post call of the it() in the describe block. Although, I am using done() , still I am getting below error on terminal :
error: timeout of 2000ms exceeded. for async tests and hooks, ensure "done()" is called; if returning a promise, ensure it resolves error: timeout of 2000ms exceeded. for async tests and hooks, ensure "done()" is called; if returning a promise, ensure it resolves
What am I doing wrong?
Below is my code :
describe('Testing get and post APIs', ()=> {
it('Series of get and post', (done) => {
chai.request(server)
.post('/thisis/1st_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
chai.request(server)
.get('/thisis/1st_get')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
setTimeout(function() {
chai.request(server)
.post('/thisis/last_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
done();
})
},20000);
});
});
});
});
Thanks.
If you want the test runner to wait longer than the default time you'll need to change the timeout value. For example, try adding this at the beginning of your describe block:
this.timeout(30 * 1000); // wait up to 30 seconds before failing from timeout
See also: Change default timeout for mocha & https://mochajs.org/api/test#timeout
However, depending on the reason for your desired 20 second delay, extending the test timeout may be the wrong solution. If you're using the delay just to handle cases where the request promise never resolves then a better approach would be to use Promise.race. Without understanding your situation more it's hard for me to judge though.
The timeout is set to 20000 (20 seconds) but the test timeout based on error is 2000 (2 seconds). It means that we need to set larger timeout for the test itself.
describe('Testing get and post APIs', function() { // don't use () because we want to use `this` in the next line
this.timeout(40000); // set timeout here
it('Series of get and post', function(done) {
chai.request(server)
.post('/thisis/1st_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
chai.request(server)
.get('/thisis/1st_get')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
setTimeout(function () {
chai.request(server)
.post('/thisis/last_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
done();
})
}, 20000);
});
});
});
});
I wonder whether we can do the test like below. It is cleaner and more maintainable.
describe('Testing get and post APIs', function () { // don't use () because we want to use `this` in the next line
this.timeout(40000); // set timeout here
it('Series post', function () { // no need done() because we can just return promise
return chai.request(server)
.post('/thisis/1st_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
})
});
it('Series get', function () {
return chai.request(server)
.get('/thisis/1st_get')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
});
});
it('Series last post', function(done) {
setTimeout(function () {
chai.request(server)
.post('/thisis/last_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
done();
});
}, 20000);
});
});
Hope it helps.

Resources