mocha - Retrieve result of promise test - javascript

I want to get the result of promise in before
describe('unsubscribe', function() {
var arn;
this.timeout(10000);
before(function(done) {
sns.subscribe('param1', 'param2').then(
(result) => {
arn = result;
done();
},
(error) => {
assert.fail(error);
done();
});
});
it('("param") => promise returns object', function() {
const result = sns.unsubscribe(arn);
expect(result).to.eventually.be.an('object');
});
});
Similarly, in after I want to get result of promise in test
describe('subscribe', function() {
var arn;
this.timeout(10000);
it('("param1","param2") => promise returns string', function(done) {
sns.subscribe('param1', 'param2').then(
(result) => {
arn = result;
expect(result).to.be.a('string');
},
(error) => {
assert.fail(error);
done();
});
});
after(function(done) {
sns.unsubscribe(arn).then(
(result) => done());
});
});
Is this code properly written? Is there any better practice? What is the recommended way to do so?

Every place you want to have Mocha wait for a promise to be resolved you should just return the promise rather than use done. So your before hook can be simplified to:
before(() => sns.subscribe('param1', 'param2')
.then((result) => arn = result));
This is much more readable than having done here and there and having to do anything special for error conditions. If there is an error, the promise will reject and Mocha will catch it and report an error. There's no need to perform your own assertions.
You have a test and an after hook that could also be simplified by just returning the promises they use rather than using done. And if you test depends on a promise, remember to return it. You've forgotten it in one of your tests:
it('("param") => promise returns object', function() {
const result = sns.unsubscribe(arn);
// You need the return on this next line:
return expect(result).to.eventually.be.an('object');
});
Tip: if you have a test suite in which all tests are asynchronous, you can use the --async-only option. It will make Mocha require all tests to call done or return a promise and can help catch cases where you forget to return a promise. (Otherwise, such cases are hard to if they don't raise any error.)
Defining a variable in the callback to describe and setting it in one of the before/beforeEach hooks and checking it in the after/afterEach hooks is the standard way to pass data between hooks and tests. Mocha does not offer a special facility for it. So, yes, if you need to pass data which is the result of a promise you need to perform an assignment in .then like you do. You may run into examples where people instead of using a variable defined in the describe callback will set fields on this. Oh, this works but IMO it is brittle. A super simple example: if you set this.timeout to record some number meaningful only to your test, then you've overwritten the function that Mocha provides for changing its timeouts. You could use another name that does not clash now but will clash when a new version of Mocha is released.

Related

Wait for an own function (which returns a promise) before tests are executed

I'm new to cypress and am trying to figure out how things work.
I have my own function (which calls a test controller server to reset the database). It returns a promise which completes when the DB have been successfully resetted.
function resetDatabase(){
// returns a promise for my REST api call.
}
My goal is to be able to execute it before all tests.
describe('Account test suite', function () {
// how can I call resetDb here and wait for the result
// before the tests below are invoked?
it('can log in', function () {
cy.visit(Cypress.config().testServerUrl + '/Account/Login/')
cy.get('[name="UserName"]').type("admin");
cy.get('[name="Password"]').type("123456");
cy.get('#login-button').click();
});
// .. and more test
})
How can I do that in cypress?
Update
I've tried
before(() => {
return resetDb(Cypress.config().apiServerUrl);
});
But then I get an warning saying:
Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise
I'm not invoking cy in resetDb().
Cypress have promises (Cypress.Promise), but they are not real promises, more like duck typing. In fact, Cypress isn't 100% compatible with real promises, they might, or might not, work.
Think of Cypress.Promise as a Task or an Action. They are executed sequentially with all other cypress commands.
To get your function into the Cypress pipeline you can use custom commands. The documentation doesn't state it, but you can return a Cypress.Promise from them.
Cypress.Commands.add('resetDb', function () {
var apiServerUrl = Cypress.config().apiServerUrl;
return new Cypress.Promise((resolve, reject) => {
httpRequest('PUT', apiServerUrl + "/api/test/reset/")
.then(function (data) {
resolve();
})
.catch(function (err) {
reject(err);
});
});
});
That command can then be executed from the test itself, or as in my case from before().
describe('Account', function () {
before(() => {
cy.resetDb();
});
it('can login', function () {
// test code
});
})
You can use cy.wrap( promise ), although there might still be a bug where it never times out (haven't tested).
Otherwise, you can use cy.then() (which is undocumented, can break in the future, and I'm def not doing any favors by promoting internal APIs):
cy.then(() => {
return myAsyncFunction();
});
You can use both of these commands at the top-level of spec like you'd use any command and it'll be enqueued into cypress command queue and executed in order.
But unlike cy.wrap (IIRC), cy.then() supports passing a callback, which means you can execute your async function at the time of the cy command being executed, not at the start of the spec (because expressions passed to cy commands evaluate immediately) --- that's what I'm doing in the example above.

Getting 'Timeout of 2000ms exceeded' error in mocha

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

promise & mocha: done() in before or not?

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.

What is the difference between calling chai-as-promised with or without a notify method?

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.

How do I fail a Node unit test on the catch of a Promise?

I'm doing some unit tests using Node.js and I want to fail a test like this:
doSomething()
.then(...)
.catch(ex => {
// I want to make sure the test fails here
});
I'm using Assert, so I found Assert.Fails. The problem is that fails expects actual and expected, which I don't have. The Node documentation doesn't say anything about them being required, but the Chai documentation, which is Node compliant, say they are.
How should I fail a test on the catch of a promise?
You can use a dedicated spies library, like Sinon, or you can implement a simple spy yourself.
function Spy(f) {
const self = function() {
self.called = true;
};
self.called = false;
return self;
}
The spy is just a wrapper function which records data about how the function has been called.
const catchHandler = ex => ...;
const catchSpy = Spy(catchHandler);
doSomething()
.then(...)
.catch(catchSpy)
.finally(() => {
assert.ok(catchSpy.called === false);
});
The basic principle is to spy on the catch callback, then use the finally clause of your promise to make sure that the spy hasn't been called.
If you will use mocha, then elegant way would be as following:
describe('Test', () => {
it('first', (done) => {
doSomething()
.then(...)
.catch(done)
})
})
If your Promise fail, done method will be invoked with the thrown exception as a parameter, so code above is equivalent to
catch(ex => done(ex))
In mocha invoking done() with the parameter fails the test.
Have you considered Assert.Ok(false, message)? It's more terse.
Assert.fail is looking to do a comparison and display additional info.

Categories

Resources