The below code all functions correctly with the exception of the assertion. When the test is run Jasmine reports 0 assertions. How can I keep my assertions within the promises and have them be recognized?
it("should open save NTP server modal", function (done) {
var addModal = driver.findElement(By.className('modal-dialog'));
driver.wait(until.stalenessOf(addModal), 5000).then(function () {
return driver.wait(until.elementIsEnabled(driver.findElement(By.id('saveButton'))), 5000).then(function (element){
return element.click();
});
});
driver.findElement(By.className("modal-body")).then(function (element) {
return expect(element.isDisplayed()).toBeTruthy();
});
done();
});
I know that in this specific case(my best example test) I could just catch the element and then do the expect outside of a promise:
var element = driver.findElement(By.className("modal-body"));
expect(element.isDisplayed()).toBeTruthy();
Unfortunately I have other cases where I cannot determine a way to do the exception outside of a promise.
You have to put your "done" method inside the final callback.
driver.findElement(By.className("modal-body")).then(function (element) {
expect(element.isDisplayed()).toBeTruthy();
done();
});
You can also look into Chai promises library, which is meant for handling async expects.
Related
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.
I suspect based on other responses (like Jasmine: test a setTimeout function throws an error and How to test a function which has a setTimeout with jasmine?) that there's no good solution here, but I want to figure out how to better deal with the second test below:
describe('Tests', function () {
it('succeed', function () { expect(true).toBeTruthy(); });
it('have problems', function(done) {
setTimeout(function() { console.log(undefined.foo); });
setTimeout(done, 5);
});
it('fail', function() { fail; });
it('also fail', function() { fail; });
});
Jasmine's current behavior here is to run the first test and then bail out when the rogue exception-causing setTimeout is encountered in the second test; the last two failing specs never run.
My use case doesn't look like this, of course! The asynchronous error is happening somewhere down a call stack, over a river, and through the woods. And it's obviously an error that it happens and isn't caught! But it's unfortunate if such errors will always terminate Jasmine itself, rather than causing a test case failure.
I believe you want try...catch or promises alongside Jasmine's done.fail().
Promises:
it('gets some huge file', done => {
getFile('http://movies.com/holymountain.mp4')
.then(res => {
expect(res.status).toBe(200)
done()
})
.catch(done.fail)
})
Try/catch
it('gets some huge file', done => {
try {
getFile('http://movies.com/holymountain.mp4')
expect(getFile.status).toBe(200)
done()
} catch (e) {
// console.log(e) // ???
done.fail()
}
})
Issue for reference
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.
I'm looking for a solution to define Mocha tests after getting data asynchronously.
For now, I use gulp-webdriver to getting HTML content with Selenium.
And I want tests certain HTML tags structure.
For example, I want to get all buttons structure from an HTML page.
1° In Mocha Before(), I get buttons :
var buttons = browser.url("url").getHTML("button");
2° And after that, I want tests each button in a separate it :
buttons.forEach(function(button) { it() });
The only solution found is loading HTML and extract buttons with Gulp before launch Mocha test with data_driven or leche.withData plugin.
Do you know another solution directly in Mocha test definition?
Thanks in advance,
It doesn't seem possible to dynamically create it() tests with mocha.
I finally organise my test like this :
it('Check if all tag have attribute', function() {
var errors = [];
elements.forEach(function(element, index, array) {
var $ = cheerio.load(element);
var tag = $(tagName);
if (tag.length) {
if (!tag.attr(tagAttr)) errors.push(element);
}
});
expect(errors).to.be.empty;
}
}
You can actually create dynamic It() tests with mocha if you don't mind abusing the before() hook a bit:
before(function () {
console.log('Let the abuse begin...');
return promiseFn().
then(function (testSuite) {
describe('here are some dynamic It() tests', function () {
testSuite.specs.forEach(function (spec) {
it(spec.description, function () {
var actualResult = runMyTest(spec);
assert.equal(actualResult, spec.expectedResult);
});
});
});
});
});
it('This is a required placeholder to allow before() to work', function () {
console.log('Mocha should not require this hack IMHO');
});
Mocha supports two ways to handle asynchronicity in tests. One way is using the done callback. Mocha will try to pass a function into all of your its, befores, etc. If you accept the done callback, it's your responsibility to invoke it when your asynchronous operation has completed.
Callback style:
before(function(done) {
browser.url("url").getHTML("button").then(function() {
done();
});
});
The other approach is to use Promises. Since your call to getHTML returns a Promise, you could just return that promise and Mocha would know to wait for the promise to settle before moving ahead with anything.
Here is an example of the Promise style:
before(function() {
return browser.url("url").getHTML("button");
});
A couple things worth noting about this:
- getHtml() returns a promise for the html buttons. Whenever the asynchronous call to getHTML completes, the function passed into the then function gets invoked and the resulting value from the call to getHTML is passed in.
- Returning that promise in the before lets mocha know that you're doing something asynchronous. Mocha will wait for that promise to settle before moving past your 'before'.
For your specific example, you might want to try is something like this:
describe('the buttons', function() {
var buttons;
before(function() {
return browser.url("url").getHTML("button").then(function(result) {
buttons = result;
};
});
it('does something', function() {
buttons.forEach(function(button) {
});
});
});
I have following method:
EventHandler.prototype.handle = function (event) {
var me = this;
return me.processEvent(event).then(function () {
return me.saveLastSeenEventRevision(event);
}).catch(function (err) {
me.logger.debug("First attempt of event process failed, trying recovery");
return me.recoveryStrategy.execute(event,me.processEvent.bind(me)).then(function() {
return me.saveLastSeenEventRevision(event);
});
});
};
And I have this test written:
describe('when processEvent fails', function() {
beforeEach(function () {
instance.processEvent.returns(Bluebird.reject(new Error('rejection')));
});
describe('when recovery fails', function() {
beforeEach(function () {
instance.recoveryStrategy.execute.returns(Bluebird.reject(new Error('recovery rejected')));
});
it('should not save the revision', function(done) {
instance.handle(event).catch(function() {
sinon.assert.notCalled(instance.saveLastSeenEventRevision);
done();
}).done();
});
});
});
I simulate rejection of processEvent method, and in catching function I simulate final rejection of my recovery implementation. I have more test written for this method, but the setup is the same. All test passes as they are supposed to, but in console I see this message:
Possibly unhandled Error: rejection
Is there any other way of handling these promises to pass this test?
Test setup - mocha / chai / sinon, as promise library is used bluebird.
Well, you're creating an explicit rejection but are not handling it here:
instance.processEvent.returns(Bluebird.reject(new Error('rejection')));
Bluebird sees you're creating an unhandled rejection here so it's letting you know. This is because a real function would return the rejection when called but sinon is creating it early - so the app has a pending place of error that is not handled. You can turn that off with:
Bluebird.onPossiblyUnhandledRejection(function(){});
Or better, use a tool like sinon-as-promised which lets you do:
instance.processEvent.rejects('rejection');
On a side note - don't use the silly done syntax, Mocha has built in promises support. You can return promises from test.
So, problem was with sinon stubs and setting up return values. It seems, that passing rejected promise as return value is some how evaluated inside sinon itself.
There are two options:
to not set return value directly, but implement function which will be used instead of the stub one
sinon.stub(instance, 'processEvent', function() {
return Bluebird.reject(new Error('rejected');
});
to use sinon-as-promised library, which allows following:
sinon.stub(instance, 'processEvent').rejects(new Error('rejected'));