Test sockets with mocha and sinon - javascript

Background
I am made a small function that emits messages via sockets and I am trying to test it using mocha.
const myFun = socket => {
socket.emit("first", "hello World!");
setTimeout(() => {
socket.emit("second", "hello Moon!");
}, 1000);
setTimeout(() => {
socket.emit("third", "hello Mars? Venus? I dunno...");
}, 2000);
};
Because I inject the socket dependency into my function which uses it, it is fairly simple to just pass it a fake socket and test if it is called and with which parameters (using, for example, sinon.js).
Problem
The problem here is that I don't know when my test ends. Because myFun does not return a promise nor anything I don't know how to tell mocha that I have sent all the messages I wanted and that the test should end.
Test
Currently, I am using the following code:
const chai = require("chai");
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
describe("myFun", () => {
const fakeSocket = {
emit: sinon.spy()
};
it("receive first message", done => {
myFun(fakeSocket);
setTimeout(() => {
try{
expect(fakeSocket.emit).to.have.been.calledThrice;
done();
}catch(err){
done(err);
}
}, 3000);
//we wait 3 seconds because it should be more than enough for all messages to be delivered
});
});
I am using a setTimeout with a try catch to test the code, which honestly is quite horrible.
Question
How can I improve my tests so they don't take X seconds and just end when I receive the last message?
How do I remake my tests so I don't depend on timers ?

You can use fake timers to advance the clock virtually:
describe("myFun", () => {
const fakeSocket = { emit: sinon.spy() }
beforeEach(() => {
this.clock = sinon.useFakeTimers();
});
afterEach(() => {
fakeSocket.emit.reset();
this.clock.restore();
});
it('emit first message', () => {
myFun(fakeSocket);
expect(fakeSocket.emit).to.have.been.calledOnce;
});
it('emit second message after 1000ms', () => {
myFun(fakeSocket);
this.clock.tick(1000);
expect(fakeSocket.emit).to.have.been.calledTwice;
});
it('emit third message after 2000ms', () => {
myFun(fakeSocket);
this.clock.tick(2000);
expect(fakeSocket.emit).to.have.been.calledThrice;
});
});
Or without any timers at all (making setTimeout synchronously call the callback it gets passed):
describe("myFun", () => {
const fakeSocket = { emit: sinon.spy() }
const stub = sinon.stub(global, 'setTimeout').callsArg(0);
after(() => {
stub.restore();
});
it('should emit three messages', () => {
myFun(fakeSocket);
expect(fakeSocket.emit).to.have.been.calledThrice;
});
});

How can I improve my tests so they don't take 3 seconds and just end when I receive the last message?
Which message is the last message? Right now you emit three messages, but what if myFun is changed to emit four messages? Or four-hundred? Mocha can't know that myFun is finished when async is involved unless you tell it.
Perhaps you should socket.emit('done') once you have finished emitting all your messages, and then you can wait for that particular emit to mark your test as complete.

What you need to look at is Sinon fake timers.
Fake timers in Sinon allow you to control the output of inbuilt timing functions like the Date constructor, and setTimeout/setInterval.
A basic usage in mocha might be something like:
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers();
});
it ('should do a thing', () => {
//assertion
});
it ('should a thing 1000 ms later'), () => {
clock.tick(1000);
//assertion
});
afterEach(() => {
clock.restore();
});

Related

Node: how to test multiple events generated by an async process

I need to test an async Node.js library which periodically generates events (through EventEmitter) until done. Specifically I need to test data object passed to these events.
The following is an example using mocha + chai:
require('mocha');
const { expect } = require('chai');
const { AsyncLib } = require('async-lib');
describe('Test suite', () => {
const onDataHandler = (data) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
};
it('test 1', async () => {
const asyncLib = new AsyncLib();
asyncLib.on('event', onDataHandler); // This handler should be called/tested multiple times
await asyncLib.start(); // Will generate several 'events' until done
await asyncLib.close();
});
});
The problem is that even in case of an AssertionError, mocha marks the test as passed and the program terminates with exit code 0 (instead of 1 as I expected).
The following uses done callback instead of async syntax, but the result is the same:
require('mocha');
const { expect } = require('chai');
const { AsyncLib } = require('async-lib');
describe('Test suite', () => {
const onDataHandler = (data) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
};
it('test 1', (done) => {
const asyncLib = new AsyncLib();
asyncLib.on('event', onDataHandler);
asyncLib.start()
.then(asyncLib.close)
.then(() => done());
});
});
I have also tried with a "pure" Node.js approach using the native assert.ok without any 3rd part library:
const { strict: assert } = require('assert');
const { AsyncLib } = require('async-lib');
const test = async () => {
const onDataHandler = (data) => {
assert.ok(data.foo != null);
assert.ok(data.bar != null);
assert.ok(data.bar.length > 0);
};
asyncLib.on('event', onDataHandler);
const asyncLib = new AsyncLib();
await asyncLib.start();
await asyncLib.close();
}
(async () => {
await test();
})();
Even in this case, an AssertionError would make the program to terminate with exit code 0 instead of 1.
How can I properly test this code and make the tests correctly fail in case of an assertion error?
There are some things that you need to fix to make it works:
Make your test async, because the test is going to execute the expects after a certain event is received meaning it's going to be asyncronous.
Your event handler in this case onDataHandler should receive the done callback because there is the way how you can indicate to mocha that the test was finished successful as long as the expects don't fail.
I wrote some code and tested it out and it works, you have to make some changes to adapt your async library though:
describe('Test suite', function () {
const onDataHandler = (data, done) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
done();
};
it('test 1', async function (done) {
eventEmitter.on('event', (data) => onDataHandler(data, done));
setTimeout(() =>{
eventEmitter.emit('event', {
})
}, 400)
});
});

How do I force timeouts in Jest to test timeout errors?

We have a function in our React project to fetch a list of stores. If the fetch takes longer than 10 seconds we abort the request and show an error.
const controller = new AbortController();
const getTimeout = setTimeout(() => controller.abort(), 10000);
const fetchStores = storeId => (
ourFetchStoresFunction(`http://my-api/${storeId}`, {
headers: { 'x-block': 'local-stores' },
signal: controller.signal
})
.then((results) => {
clearTimeout(getTimeout);
return results
})
.catch((err) => { throw err; })
);
Here is the basic test for the fetchStores function:
it('fetchStores should return a stores array', () => {
storesAPI.fetchStores(MOCK_STORES)
.then((stores) => {
expect(Array.isArray(stores)).toBe(true);
})
.catch();
});
How do I mock this timeout in Jest?
using setTimeout in the .then block while calling that method did not work. Additionally, I would rather NOT have to wait 10 seconds during the test. I looked up jest.useFakeTimers but got nowhere.
I could test global timers, read here: jestjs.io/docs/timer-mocks You can use jest spyOn function. For example:
let timeoutSpy;
beforeEach(() => {
timeoutSpy = jest.spyOn(global, 'setTimeout');
});
afterEach(() => {
jest.restoreAllMocks();
})
Then in your test, you can expect(tymepoutSpy).toHaveBeenCalledTimes(1); Something like this should work, worked in my case.
In your case, you can mock about controller too, again using jest.spyOn, could be something like this:
let abortSpy;
In the same beforeEach you can set it:
abortSpy = jest.spyOn(AbortController.prototype, 'abort');
And I suppose ourFetchStoresFunction function is similar to fetch?
And finally, you can mock fetch function, so it takes lots f time to load, it can be done in this way:
> global.fetch = jest.fn(() => Promise.resolve({ // set some time })
> );

How to wait my custom command finish, then executing remaining Cypress commands

I'm getting trouble with Cypress asynchronous mechanism. I have a custom command that is placed in this file
class HeaderPage {
shopLink = 'a[href="/angularpractice/shop"]'
homeLink = ''
navigateToShopPage() {
cy.get(this.shopLink).click()
}
sshToServer() {
setTimeout(() => {
console.log('Connecting')
}, 5000)
console.log('Connected')
}
}
export default HeaderPage
The function sshToServer is simulated to pause 5000ms. So I want Cypress remaining test will be hold and wait for this function completed. How can I do that? Thanks in advance.
import HeaderPage from "../support/pageObjects/HeaderPage"
describe('Vefiry Alert and Confirm box', () => {
const headerPage = new HeaderPage()
it('Access home page', () => {
cy.visit(Cypress.env('url') + 'AutomationPractice/')
});
it('SSH to server', () => {
headerPage.sshToServer()
});
it('Verify content of Alert', () => {
cy.get('#alertbtn').click()
cy.on('window:alert', (alert) => {
expect(alert).to.equal('Hello , share this practice page and share your knowledge')
})
});
You can issue a new cy.wrap command with a value null and calling your async function in the .then function. Cypress will resolve that async function automatically and then move on to the next test.
First, you should convert your sshToServer method to an async(promise) function:
sshToServer() {
console.log('Connecting');
return new Promise((resolve) => {
setTimeout(() => {
console.log('Connected');
resolve();
}, 5000);
});
}
Then, in your spec:
it('SSH to server', { defaultCommandTimeout: 5000 }, () => {
cy.wrap(null).then(() => headerPage.sshToServer());
});
Note that I have also used a bigger for the spec defaultCommandTimeout since the default timeout is 4 seconds which is shorter than your sshToServer method and would cause the test to fail if not using a bigger timeout.
Sorry for late answer but i think this is the easiest way to do it
cy.wait(milliseconds)

How to force Jest unit test assertion to execute before test ends?

I have the following test in my react native application, but the test should fail (because the action returned is not equal to the action I put in expectedActions. My guess is that it is passing because the expect test runs after the test has completed.
How can I force the test to wait until the promise is completed and the expect test runs? Is there another way of doing this?
describe('authorize actions', () => {
beforeEach(() => {
store = mockStore({});
});
it('should create an action to signify successful auth', () => {
auth.authorize.mockImplementation(() => Promise.resolve({"something": "value"}));
const expectedActions = [{"type":"AUTHORIZE_RESPONSE","payload":{"something":"sdklfjsdf"}}];
authorizeUser(store.dispatch, store.state).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
})
});
Ok, looks like I just missed some of the Jest docs - if you return the promise, i.e. return auth.authorize.mockImplementation(() => Promise.resolve... then Jest will wait until it's completed before continuing.
The are varios ways to test async code. Check the docs for examples: https://facebook.github.io/jest/docs/en/asynchronous.html
One could be returning the promise:
describe('authorize actions', () => {
beforeEach(() => {
store = mockStore({});
});
it('should create an action to signify successful auth', () => {
auth.authorize.mockImplementation(() => Promise.resolve({"something": "value"}));
const expectedActions = [{"type":"AUTHORIZE_RESPONSE","payload":{"something":"sdklfjsdf"}}];
return authorizeUser(store.dispatch, store.state).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
})
});

How can I test an async event emitter using mocha and TypeScript?

I'm having trouble getting an async event emitter test running in mocha v5.0.4. I'm using a typed Rx based event emitter and node.js v8.10. Can anyone point me in the right direction please?
The first and the second it-blocks work fine. However, the third it-block is never invoked. Mocha doesn't give me a test result (no output at all, mocha runs into the timeout which I set to 10 minutes)
describe('xyz', () => {
const xyz : Xyz = new Xyz(config);
describe('make()', () => {
let emitter : Emitter<Status>;
emitter = xyz.make("abc", 0.01);
it('should return an Emitter', () => {
expect(emitter).to.be.a('Object');
});
it('should eventually return a foo', (done) => {
emitter.on('foo').subscribe( foo => {
expect(foo).to.be.a('foo');
done();
})
});
it('should eventually return a bar', (done) => {
emitter.on('bar').subscribe( bar => {
console.log('foobar!');
expect(bar).to.be.a('bar');
done();
});
});
});
});
What strikes me most is that the event is definitely fired. I can see foobar! as the console output. I'm not using Spies but sticking to the second example from the mocha documents.

Categories

Resources