When using supertest for testing async HTTP requests in JavaScript, what's the difference between these two snippets? Is one of them correct and the other one wrong?
request('http://localhost:8080/').get('/api/people')
.expect(res => res.body.should.have.length(5))
vs.
request('http://localhost:8080/').get('/api/people')
.then(res => res.body.should.have.length(5))
The only differences I could notice were:
expect returns a Test object and, when test fails, prints a large stack trace
then returns a Promise object and, when test fails,
prints a small stack trace
Depending on what test runner you're using will obviously affect the answer, but something like Mocha will allow you to return the Promise directly in your test and this will wait to be resolved before the test passes.
So if you having something like:
describe('Your test case', function () {
it('will wait for promise to resolve', function () {
return request('http://localhost:8080/').get('/api/people')
.then(res => res.body.should.have.length(5))
})
})
Whereas in the other instance you really should use a done callback as per https://www.npmjs.com/package/supertest docs.
Related
I have a class which contains methods.Now after initializing the class i want to invoke the methods but my test flow is not to the method and getting error like Uncaught error outside test suite.Below is my code
describe('Test Stu positive', () => {
it("Test Stu positive", (done) => {
const stuDetails = new masterDetails(getdetails);
expect(stuDetails.retrieveStudent(res => {
console.log(res);
done()
}))
});
});
Now, in the above code i am not able to print console.log(res);. What i am doing wrong?
I believe that you are using Mocha as your testing framework and looks like the error is not being handled by mocha, because is an asynchronous operation and you are not passing the error to the done callback method as described in the mocha documentation
it is really hard to tell how your function works, if it returns a promise or if it just uses a callback and the error is handled inside the function, so I can't provide you a code example on how to accomplish this. if you mind providing your function declaration I can update my answer with an example solution.
This question already has answers here:
Testing with React's Jest and Enzyme when simulated clicks call a function that calls a promise
(2 answers)
Closed 4 years ago.
I am testing a component that uses an external Api.js file. When testing the component, I'm mocking the function using
import {
apiCall,
} from './Api';
jest.mock('./Api');
I want to test a case where the Api call fails, to check that my component shows the correct error. In order to do that, I'm faking the Api response to be:
apiCall.mockImplementation(
() => Promise.reject({ error: 'This is my error' }),
);
However, when mocking the response in this way, I am not able to make it execute before the test case is evaluated. Is there any way to wait for that response to happen before finishing the test?
I have created a codesandbox as simple as I can showing the issue. Since seems that codesandbox does not allow to use jest.mock, I'm just making the original API call to return Promise.reject.
https://codesandbox.io/s/6y0rn74mzw
The behaviour is simple: A text "No error" and a button, that when clicked it calls to the API method that automatically returns Promise.reject. In the response, we change the text to "There is an error". The first test just looks for the word 'error' to make the test pass and show the full evaluation of the code (the test stops if something fails), and the second test is the test I would expect to pass if the correct order was applied.
What would be the way to assure the proper execution order in the test case?
When it comes to dealing with Promises in jest tests, what I've done is as follows:
it('test script description', (done) => {
apiCall()
.then((response) => {
// Verify the response from the promise
done();
});
});
I'm obviously not saying that this is the best solution (perhaps someone can point you in a better direction) but it's the only one that worked for me!
So the solution is to use setImmediate.
If I change the test case to be:
it("should pass but fails due to execution order", done => {
console.log("-----------");
expect(true).toBe(true);
const wrapper = mount(<App />);
wrapper.find("button").simulate("click");
setImmediate(() => {
const newWrapper = wrapper.update();
expect(newWrapper.html()).toContain("There is an error");
done();
});
console.log("Third");
console.log("-----------");
});
It passes.
A good explanation here: https://github.com/kentcdodds/react-testing-library/issues/11
So i have this code below. It deletes the db and adds two users for test case.
when i verify it manually in mongo database everything shows correct but in mocha test case I get the timeout error even after defining the done argument and calling it.
Please help me on this.
const users = [{
_id: new ObjectID(),
email: 'First'
}, {
_id: new ObjectID(),
email: 'Second'
}];
beforeEach((done) => {
User.remove({}).then(() => {
return User.insertMany(users);
}).then(() => done());
})
In mocha, tests will time out after 2000 ms by default. Even if you were handling the asynchrony 100% correctly (which you are not), if you do an async operation that takes longer than 2 seconds, mocha will assume a failure. This is true even if the async operation is in a beforeEach or some other hook.
To change this, you need to invoke the timeout method on the test instance, giving it a sufficiently-high value. To access the test instance, you need to define your test functions using the function keyword rather than arrow syntax, and it will be available as this in your test functions:
beforeEach(function(done) {
this.timeout(6000); // For 6 seconds.
User.remove({}).then(() => {
return User.insertMany(users);
}).then(() => done());
});
In what way could you handle the asynchrony here better, though? As Henrik pointed out in comments, you'll never call done if either of your database calls fail. To be honest, though, since you're already dealing with promises, you shouldn't even use the done callback. Instead, just use Mocha's built-in promise support by returning the chained promise.
beforeEach(function() {
this.timeout(6000); // For 6 seconds.
return User.remove({})
.then(() => User.insertMany(users));
});
This way, if either of those promises rejects, Mocha will know and will show the rejection instead of just sitting around waiting for your test to time out.
You can even use async/await instead if you prefer:
beforeEach(async function() {
this.timeout(6000); // For 6 seconds.
await User.remove({});
await User.insertMany(users);
});
I am reading some tutorials on promise tests in mocha. There is a piece of codes:
before(function(done) {
return Promise.resolve(save(article)).then(function() {
done();
});
});
Why done() called in the then() in the before()? What is the difference between the above codes and the following codes:
before(function(done) {
return Promise.resolve(save(article));
});
Thanks
UPDATE
My question is to compare with the following codes:
before(function() {
return Promise.resolve(save(article));
});
Sorry for the typo.
The first code snippet with the before hook returns a promise and calls done. In Mocha 3.x and over, it will result in this error:
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
It used to be that it did not particularly matter if you used done and returned a promise, but eventually the Mocha devs figured that specifying both done and returning a promise just meant the test designer made a mistake and it was better to have Mocha pitch a fit rather than silently allow it.
In your 2nd snippet, you have the done argument and return a promise but Mocha will still wait for done to be called and will timeout. (It really should detect the argument and raise an error like in the 1st case, but it doesn't...)
Generally, if you are testing an asynchronous operation that produces a promise, it is simpler to return the promise than use done. Here's an example illustrating the problem:
const assert = require("assert");
// This will result in a test timeout rather than give a nice error
// message.
it("something not tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
});
});
// Same test as before, but fixed to give you a good error message
// about expecting a value of 2. But look at the code you have to
// write to get Mocha to give you a nice error message.
it("something tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
}).catch(done);
});
// If you just return the promise, you can avoid having to pepper your
// code with catch closes and calls to done.
it("something tested properly but much simpler", () => {
return Promise.resolve(1).then((x) => {
assert.equal(x, 2);
});
});
With regards to the completion of asynchronous operations, it works the same whether you are using it, before, beforeEach, after or afterEach so even though the example I gave is with it, the same applies to all the hooks.
I am not sure if I understood 100% the question, but the tests will not start until done is called.
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
This test will not start until the done function is called in the call to beforeEach above. And this spec will not complete until its done is called.
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done();
});
You don't have to pass done in your example, just:
before(function() {
return Promise.resolve(save(article));
});
If you do pass done the test runner will expect to be called before continue, otherwise it will probably throw a timeout error.
In this particular case there is no functional difference. The callback, often called done, was introduced to handle asynchronous tests when using callbacks. Returning a Promise is sufficient, but note that you cannot define the done callback, because the test suite will wait until it's called. Use done when you can't easily return a Promise. In your case the second test will be infinite, because you define done, which you never actually call.
I'm using chai and chai-as-promised to test some asynchronous JS code.
I just want to check that a function returning a promise will eventually return an Array and wrote the 2 following tests:
A:
it('should return an array', () => {
foo.bar().should.eventually.to.be.a('array')
})
B:
it('should return an array', (done) => {
foo.bar().should.eventually.to.be.a('array').notify(done)
})
Both are passing OK, but only the B option actually runs the full code included in my bar() function (ie displaying the console.log() message from the code below). Am I doing something wrong? Why is that so?
bar() {
return myPromise()
.then((result) => {
console.log('Doing stuff')
return result.body.Data
})
.catch((e) => {
console.err(e)
})
}
What test library do you use? Mocha, Intern or other?
For Mocha and Intern, you must return the promise from your test method:
it('should return an array', () => {
return foo.bar().should.eventually.to.be.a('array');
})
Testing a promise means that you’re testing asynchronous code. Notify and done callback sets up a timer and waits for the promise chain to finish up executing.
The second approach is the correct one since you may need to test chained promises.
Take a look at this tutorial which got me into asynchronous unit testing.