How to mock readline.on('SIGINT')? - javascript

I have this piece of code:
function getMsg() {
return new Promise(function (resolve, reject) {
var input = [];
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', function (cmd) {
if (cmd.trim()) {
input.push(cmd);
} else {
rl.close();
}
});
rl.on('close', function () {
rl.close();
resolve(input.join('\n'));
});
rl.on('SIGINT', reject);
});
}
I'm trying to test this function, my attempt, so far, is this:
it('should reject if SIGINT is sent', function () {
sandbox.stub(readline, 'createInterface', function () {
return {
on: function (action, callback) {
callback();
},
prompt: function () {},
close: function () {}
};
});
return getMsg().then(null).catch(function () {
expect(true).to.be.equal(true);
});
});
But of course, that doesn't simulate a SIGINT, how do I do this?

I think you need a different setup:
const EventEmitter = require('events').EventEmitter
...
it('should reject if SIGINT is sent', function () {
let emitter = new EventEmitter();
sandbox.stub(readline, 'createInterface', function () {
emitter.close = () => {};
return emitter;
});
let promise = getMsg().then(function() {
throw Error('should not have resolved');
}, function (err) {
expect(true).to.be.equal(true);
});
emitter.emit('SIGINT');
return promise;
});
The object returned by readline.createInterface() inherits from EventEmitter, so that's what the stub will return. The additional close function is simply added to it to prevent errors when it gets called.
You can't return the promise returned by getMsg directly, because that doesn't give you a chance to emit the SIGINT "signal" (really just an event, but for testing purposes it'll work just fine). So the promise is stored.
The test should fail with the "fulfilled" handler gets called, which had to be done explicitly, otherwise Mocha thinks the test passed.
Next, the SIGINT is sent (which should trigger the rejection handler as intended), and the promise is returned.

Related

How to test a method with timeout after calling another method in sinon

How can I test a property inside a timeout from another called method?
I want to test a property if it's changed inside the setTimeout but using sinons useFakeTimer doesn't seems to work. Or am I missing something?
To illustrate here's my code
const fs = require('fs');
function Afunc (context) {
this.test = context;
}
module.exports = Afunc;
Afunc.prototype.start = function () {
const self = this;
this.readFile(function (error, content) {
setTimeout(function () {
self.test = 'changed';
self.start();
}, 1000);
});
}
Afunc.prototype.readFile = function (callback) {
fs.readFile('./file', function (error, content) {
if (error) {
return callback(error);
}
callback(null, content);
})
}
And here's what I have so far.
describe('Afunc', function () {
let sandbox, clock, afunc;
before(function () {
sandbox = sinon.createSandbox();
});
beforeEach(function () {
clock = sinon.useFakeTimers();
afunc = new Afunc('test');
sandbox.stub(afunc, 'readFile').yieldsAsync(null);
});
afterEach(function () {
clock.restore();
sandbox.restore();
});
it('should change test to `changed`', function () {
afunc.start();
clock.tick(1000);
afunc.test.should.be.equal('changed');
});
});
after the clock.tick check the property test is not changed.
Any help is deeply appreciated! Thanks in advance.
Just change this:
sandbox.stub(afunc, 'readFile').yieldsAsync(null);
...to this:
sandbox.stub(afunc, 'readFile').yields();
...and it should work.
Details
yieldsAsync defers using process.nextTick so the callback passed to readFile wasn't getting called until "all instructions in the current call stack are processed"...which in this case was your test function.
So the callback that changed afunc.test to 'changed' was getting called...but not until after your test completed.

Unit testing logic inside promise callback

I have an ES6 / Aurelia app that I am using jasmine to test. The method I am trying to test looks something like this:
update() {
let vm = this;
vm.getData()
.then((response) => {
vm.processData(response);
});
}
Where this.getData is a function that returns a promise.
My spec file looks something like this:
describe('my service update function', () => {
it('it will call the other functions', () => {
myService = new MyService();
spyOn(myService, 'getData').and.callFake(function() {
return new Promise((resolve) => { resolve(); });
});
spyOn(myService, 'processData').and.callFake(function() { return; });
myService.update();
// this one passes
expect(myService.getData).toHaveBeenCalled();
// this one fails
expect(myService.processData).toHaveBeenCalled();
});
});
I understand why this fails - promises are asynchronous and it hasn't been resolved by the time it hits the expect.
How can I push the promises to resolve from my test so that I can test the code inside the call back?
jsfiddle of failed test: http://jsfiddle.net/yammerade/2aap5u37/6/
I got a workaround running by returning an object that behaves like a promise instead of an actual promise
describe('my service update function', () => {
it('it will call the other functions', () => {
myService = new MyService();
spyOn(myService, 'getData').and.returnValue({
then(callback) {
callback();
}
});
spyOn(myService, 'processData').and.callFake(function() { return; });
myService.update();
// this one passes
expect(myService.getData).toHaveBeenCalled();
// this one fails
expect(myService.processData).toHaveBeenCalled();
});
});
Fiddle here: http://jsfiddle.net/yammerade/9rLrzszm/2/
Is there anything wrong with doing it this way?
it((done) => {
// call done, when you are done
spyOn(myService, 'processData').and.callFake(function() {
expect(myService.processData).toHaveBeenCalled();
done();
});
})

Testing js promises with Mocha, Chai, chaiAsPromised and Sinon

I'm trying to test a function that reads in a file and returns a promise with the file's contents.
function fileContents(){
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, data){
if (err) { reject(err); }
else { resolve(data); }
});
});
}
The unit test for the above
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function () {
let fileContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').returns(function(path, callback) {
callback(null, fileContents);
});
let fileContentsPromise = fileContents();
return fileContentsPromise
.then(data => {
expect(data).to.eventually.equal(fileContents);
});
});
The above test errs with
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
I also tried
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function (done) {
let fileContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').returns(function(path, callback) {
callback(null, fileContents);
});
let fileContentsPromise = fileContents();
fileContentsPromise.then(function(data){
expect(data).to.equal(fileContents);
done();
});
});
and got the same error. The function is working in my local site, but I don't know how to write a test for it. I'm new to js. What am I missing?
There are multiple problems with your code. For instance, you redeclare fileContents in your test and assign it a string value, which of course won't work with doing fileContents() in the same test. I'm going to concentrate on two conceptual problems rather than the "duh"-type mistakes like this one.
The two conceptual problems are:
To have fs.readFile call your callback with fake values you must use .yields. Using .returns changes the return value, which you do not use. So stub it like this:
sinon.stub(fs, 'readFile').yields(null, fakeContents);
You are using the .eventually functionality provided by chai-as-promised on a non-promise, but you have to use it on a promise for it to work properly so your test should be:
return expect(fileContentsPromise).to.eventually.equal(fakeContents);
Here's code that works:
const sinon = require("sinon");
const fs = require("fs");
const chai = require("chai");
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const expect = chai.expect;
// We need to have filename defined somewhere...
const filename = "foo";
function fileContents(){
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, data){
if (err) { reject(err); }
else { resolve(data); }
});
});
}
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function () {
let fakeContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').yields(null, fakeContents);
let fileContentsPromise = fileContents();
return expect(fileContentsPromise).to.eventually.equal(fakeContents);
});
});

Jasmine: How to expect promise handler to not throw exception

I have this function:
reload() {
myService.queryData()
.done(...)
.always(() => throw "fake exception"); //just to simulate the failure
}
I want my test reload function and make sure it does not throw exception nor the promise callback does.
describe("reload", function () {
it("does not throw exception", function (done) {
spyOn(myService, "queryData").and.callFake(() => {
let deffered = $.deffered();
setTimeOut(() => deffered.reject(), 0)
return deffered.promise();
});
reload();
setTimeout(() => {
//this is evaluated after the exception has been thrown, but
//how to check whether exception has been thrown
}, 2);
});
});
EDIT: I might not be able to return a promise in some cases, where the return type of the function is already defined, e.g component's lifecycle event:
MyComponent extends React.Component {
componentDidMount() {
this.load(
galleryService.nodes().then(galleryResult => this.setState({ nodes: galleryResult.nodes }))
);
this.load(
galleryService.caches().then(cachesResult => this.setState({ caches: cachesResult.caches }))
);
}
}
var myComponent = React.createElement(MyComponent);
TestUtils.renderIntoDocument(myComponent); //this triggers the componentDidMount event and I need to make sure it won't throw error.
Let reload return the promise it creates. In your test case, attach a catch handler to it which triggers a test failure:
reload().catch(err => done.fail(err));
Update after question was edited: If you cannot change the return value of your original function then factor out the relevant parts into separate functions. For example:
function reloadNodes() {
return somePromise();
}
function reloadCaches() {
return anotherPromise();
}
function reload() {
reloadNodes();
reloadCaches();
}
You can then test reloadNodes and reloadCaches instead of reload. Obviously you don't need to create a separate function for each promise, instead combine your promises using something like Promise.all where appropriate.
I believe that spying on window.onerror is the way to go:
describe("reload", function () {
it("does not throw an exception", function (done) {
spyOn(window, 'onerror').and.callFake((error: any, e: any) => {
fail(error);
});
spyOn(myService, "queryData").and.callFake(() => {
let deffered = $.deffered();
setTimeout(deffered.reject, 0);
return deffered.promise();
});
});
setTimeout(done, 2);
});
});

Waiting for an optional process operation inside bluebird promise

I have a bluebird promise, which runs a check. If this check is true, it can continue on. However, if this check is false, it needs to spawn an asynchronous process, which it must wait to complete before continuing.
I have something like the following:
var foo = getPromise();
foo.spread( function (error, stdout, stdin) {
if (error) {
// ok, foo needs to hold on until this child exits
var child = spawn(fixerror);
child
.on('error', function (e) {
//I need to error out here
})
.on('close', function (e) {
//I need to have foo continue here
});
} else {
return "bar";
}
});
How would I go about doing that?
First off why does your .spread() handler take a callback with error as the first argument. That does not seem correct. An error should cause a rejection, not a fulfillment.
But, if that was indeed the way your code works, then you just need to return a promise from within your .spread() handler. That promise will then get chained to the original promise and you can then see when both are done:
getPromise().spread( function (error, stdout, stdin) {
if (error) {
// ok, foo needs to hold on until this child exits
return new Promise(function(resolve, reject) {
var child = spawn(fixerror);
child.on('error', function (e) {
//I need to error out here
reject(e);
}).on('close', function (e) {
// plug-in what you want to resolve with here
resolve(...);
});
});
} else {
return "bar";
}
}).then(function(val) {
// value here
}, function(err) {
// error here
});
But, probably, your .spread() handler should not have the error argument there and that should instead cause a rejection of the original promise:
getPromise().spread( function (stdout, stdin) {
// ok, foo needs to hold on until this child exits
return new Promise(function(resolve, reject) {
var child = spawn(fixerror);
child.on('error', function (e) {
//I need to error out here
reject(e);
}).on('close', function (e) {
// plug-in what you want to resolve with here
resolve(...);
});
});
}).then(function(val) {
// value here
}, function(err) {
// error here
});
wrap the spawn or any other alternative route in promise function, then keep the flow as, (sample code):
promiseChain
.spread((stderr, stdout, stdin) => stderr ? pSpawn(fixError) : 'bar')
.then(...
// example for promisified spawn code:
function pSpawn(fixError){
return new Promise((resolve, reject) => {
spawn(fixError)
.on('error', reject)
.on('close', resolve.bind(null, 'bar'))
})
}

Categories

Resources