Why does the debugger fail and test pass unless done() callback invoked? - javascript

1) Can anyone explain why, when debugging this jasmine test for hapi, the debugger never hits any breakpoint inside the injected section (see comment) unless done is called later on? How can the absence of a line of code that is not yet reached affect the debugger earlier on ?
I am aware that it is important to call the done method (which I have commented out on purpose). I am however surprised by the consequences.
2) Another unfortunate side-effect of forgetting to call the done method is that the test always passes. Instead of passing I would rather see it fail if I make an error. Any suggestions?
const server = require("../lib/server");
describe("Server hello", function () {
it("returns status code 200", function (done) {
server.inject({ method: 'GET', url: '/' }, (res) => {
// Never reached if done uncommented - even by debugger breakpoint - why?");
console.log("GOT " + res.payload);
expect(res.statusCode).toBe(200);
// done(); // Test always passes if uncommented - is there any way to force an error instead?
});
});
});

Read the source, Luke! Jasmine docs for asynchronous testing note:
This spec 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.
So if you don't call done your suite is not run, not that it runs and times out!

Related

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.

Mocha and NightmareJS- continue test

I am running into a simple issue, I have about 5 tests to be part of one group, one of them I am forcing them to fail but I am not able to exit the fail state:
.goto(url)
.wait('#element')
.evaluate(fnc...)
wait('#newelement')
....
evaluate(function(){
return document.querySlector('#myid').innerText
})
.then(function(result) {
result.should.equal('1');// I know I am expecting 2
done();
})
// will never be executed.
.then.....
// The following message is thrown.
Error: timeout of 15000ms exceeded. Ensure the done() callback is being called in this test.
It would be OK, however there are other tests that I need to make after this one fails but I can't seem to be able to continue or have an elegant way to report the failure without affecting the rest.
If you want to force a test to fail you can call done passing an error as an argument:
done(new Error('this is my error message'))
So in your case, something like this:
.goto(url)
.wait('#element')
.evaluate(fnc...)
wait('#newelement')
....
evaluate(function(){
return document.querySlector('#myid').innerText
})
.then(function(result) {
done(new Error('Please test, fail because I want you to.'));
})
// will never be executed.
.then.....
Also, as a side note, your original code might not work, because of multiple calls to evaluate on the same chain call, see this answer for more details.

Q.delay not working with Q promise library and Jasmine clock?

I have a very basic Jasmine test using Q, and it doesn't seem to be working. I'm simply using the Jasmine mock clock and trying to use Q.delay, which I think uses setTimeout under the hood.
I had some more complex tests that involved calling setTimeout() from a Q promise's then handler, and that didn't seem to work either, but I thought this would make a simpler test case to post on Stack Overflow.
Here's my very simple test case:
it('clock test', function() {
jasmine.clock().install();
var foo = null;
Q.delay('hi', 10000).then(function(arg) {
console.log('foo');
foo = arg;
});
jasmine.clock().tick(10010);
expect(foo).toEqual('hi');
jasmine.clock().uninstall();
});
(This test was based on the test case found in a similar SO question: Jasmine clock tick & Firefox: failing to trigger a Q.delay method)
When I run the test, it fails saying Expected null to equal 'hi'. The console.log never even executes.
To see if the problem was with Q or something else, I tried adding a simple setTimeout call inside the spec:
setTimeout(function() {
console.log("bar");
}, 10000);
This worked - bar was printed to the console after the call to jasmine.clock().tick.
After the Jasmine clock is uninstalled, the normal clock kicks in, and after waiting 10 seconds, then the foo gets printed out.
Anyone have any idea what's going on?
You are asking it to do async in a sync way. Don't. Try using Jasmine's async instead and drop using the clock.
it('clock test', function(done) {
var foo = null;
Q.delay('hi', 10000).then(function(arg) {
console.log('foo');
foo = arg;
expect(foo).toEqual('hi');
done();
});
}, 15000);
Anyone have any idea what's going on?
Probably Q is serious about the asynchrony guarantee given by in the Promises/A+ spec. So even when setTimeout executes earlier than expected, that is no reason to suddenly call then callbacks synchronously. It still will need to wait a tick after the promise is fulfilled.
An alternative explanation would be that Q took its private copy of setTimout during its module initialising, to prevent exactly such messing around with builtins. Instead of calling the global function, it would use its internal reference to the old function, and not be affected by jasmine.clock() at all.
You can spyOn Q.delay and avoid having your test wait for the given delay:
describe('Q.delay', function() {
it('should call Q.delay', function(done) {
spyOn(Q.makePromise.prototype, 'delay').and.returnValue(function() {
return this;
});
Q('A resolve value').delay(3000).then(done);
expect(Q.makePromise.prototype.delay).toHaveBeenCalledWith(3000);
});
});

Node.js socket.send( ) functions failing to complete before exit

In some Node.js scripts that I have written, I notice that even if the last line is a synchronous call, sometimes it doesn't complete before Node.js exits.
I have never seen a console.log statement fail to run/complete before exiting, but I have seen some other statements fail to complete before exiting, and I believe they are all synchronous. I could see why the callback of an async function would fail to fire of course in this case.
The code in question is a ZeroMQ .send() call like so:
var zmq = require('zmq');
var pub = zmq.socket('pub');
pub.bindSync('tcp://127.0.0.1:5555');
setInterval(function(){
pub.send('polyglot');
},500);
The above code works as expected...but if I remove setInterval() and just call it like this:
var zmq = require('zmq');
var pub = zmq.socket('pub');
pub.bindSync('tcp://127.0.0.1:5555');
pub.send('polyglot'); //this message does not get delivered before exit
process.exit(0);
...Then the message will not get delivered - the program will apparently exit before the pub.send() call completes.
What is the best way to ensure a statement completes before exiting in Node.js ? Shutdown hooks would work here, but I am afraid that would just be masking the problem since you can't put everything that you need to ensure runs in a shutdown hook.
This problem can also be demonstrated this way:
if (typeof messageHandler[nameOfHandlerFunction] == 'function') {
reply.send('Success');
messageHandler[nameOfHandlerFunction](null, args);
} else {
reply.send('Failure'); //***this call might not complete before the error is thrown below.***
throw new Error('SmartConnect error: no handler for ZMQ message sent from Redis CSV uploader.');
}
I believe this is a legit/serious problem because a lot of programs just need to publish messages and then die, but how can we effectively ensure all messages get sent (though not necessarily received)?
EDIT:
One (potential) way to fix this is to do:
socket.send('xyz');
socket.close(); // supposedly this will block until the above message is sent
process.exit(0);
Diving into zeromq.node, you can see what Socket.send just pushes your data to _outgoing:
this._outgoing.push([msg, flags]);
... and then calls _flush iff zmq.ZMQ_SNDMORE is unset:
this._flush();
Looks like _flush is actually doing the socket write. If _flush() fails, it emits an error.
Edit:
I'm guessing calling pub.unbind() before exiting, will force the _flush() to be called:
pub.unbind('tcp://127.0.0.1:5555', function(err) {
if (err) console.log(err);
process.exit(0); // Probably not even needed
});
I think the simple answer is the the socket.send() method is in fact asynchronous and that is why we see the behavior I described in the OP.
The question then is - why does socket.send() have to be asynchronous - could there not be a blocking/synchronous version that we could use instead for the purpose intended in the OP? Could we please have socket.sendSync()?

Unit testing window.onerror with jasmine

I am fairly new to javascript and am trying to use jasmine to unit test some error-handling code.
In particular, I'm trying to write some tests that verify that our custom code (called windowHandleError) that replaces window.onerror() gets called, and is doing what we want it to.
I've tried something along the lines of:
it("testing window.onerror", function() {
spyOn(globalerror, 'windowHandleError');
globalerror.install();
var someFunction = function() {
undefinedFunction();
};
expect(function() {someFunction();}).toThrow();
expect(globalerror.windowHandleError).toHaveBeenCalled();
});
But it doesn't trigger the onerror. There are some related questions I've looked at, but they seem to ask about specific browsers, or how/where to use onerror instead of how to test it.
window.onerror not firing in Firefox
Capturing JavaScript error in Selenium
window.onerror does not work
How to trigger script.onerror in Internet Explorer?
Based on what some of those said, I thought running the spec tests in a debugger would force the onerror to trigger, but no dice. Anyone know a better approach to this?
I recently developed small JavaScript error handler with unit tests based on Buster.JS which is similar to Jasmine.
The test that exercises the window.onerror looks like this:
"error within the system": function (done) {
setTimeout(function() {
// throw some real-life exception
not_defined.not_defined();
}, 10);
setTimeout(function() {
assert.isTrue($.post.called);
done();
}, 100);
}
It throws a real-life error within a setTimeout callback which will not stop the test execution and will check that a spy was called after 100ms in another setTimeout and then call done() which is how you test async functionality with Buster.JS. The same approach is available with Jasmine by using done() in async tests.
Without knowledge of Jasmine.
All unit tests run inside a try/catch block so that if one test dies, the next test can run (True for QUnit at least). And since window.onerror doesn't catch exceptions that is already caught inside a try/catch, it will not run when testing this in a unittest.
Try calling the onerror function manually based on the exception.
try {
//Code that should fail here.
someUndefinedFunction();
} catch (e) {
window.onerror.call(window, e.toString(), document.location.toString(), 2);
}
expect(globalerror.windowHandleError).toHaveBeenCalled();
This is far from perfect as document.location is not the same as the url argument, and you need to manually set line number. A better way would be to parse e.stack for the correct file and line number.
When calling the function like this inside a unit test, it might be a better idea to simply test that your function is set and that it functions properly when called with all faked arguments.

Categories

Resources