Sinon: Mocking websockets/ws - node.js

How can I mock up websockets/ws using Sinon? I'm trying to test that my application behaves as expected when using WebSockets, without necessarily needing to connect each time (eg: testing event handlers, etc).
Coming from a C# background, I'd just mock out the whole interface using a library like Moq, and then verify that my application had made the expected calls.
However, when trying to do this with Sinon, I'm running into errors.
An example of a test:
const WebSocket = require('ws');
const sinon = require('sinon');
const webSocket = sinon.mock(WebSocket);
webSocket.expects('on').withArgs(sinon.match.any, sinon.match.any);
const subject = new MyClass(logger, webSocket);
This class is then calling:
this._webSocket.on("open", () => {
this.onWebSocketOpen();
});
But when I try and run my tests, I get this error:
TypeError: Attempted to wrap undefined property on as function
What's the correct way to mock out an object like this using Sinon?
Thanks.

If your just trying to test if the given sockets 'on' method was called when passed in, this is how you would do it:
my-class/index.js
class MyClass {
constructor(socket) {
this._socket = socket;
this._socket.on('open', () => {
//whatever...
});
};
};
module.exports = MyClass;
my-class/test/test.js
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const sinon_chai = require('sinon-chai');
const MyClass = require('../index.js');
const sb = sinon.sandbox.create();
chai.use(sinon_chai);
describe('MyClass', () => {
describe('.constructor(socket)', () => {
it('should call the .prototype.on method of the given socket\n \t' +
'passing \'open\' as first param and some function as second param', () => {
var socket = { on: (a,b) => {} };
var stub = sb.stub(socket, 'on').returns('whatever');
var inst = new MyClass(socket);
expect(stub.firstCall.args[0]).to.equal('open');
expect(typeof stub.firstCall.args[1] === 'function').to.equal(true);
});
});
});

Related

sinon stub calls fake calling actual function

I'm having situation where I want to write unit test cases for a function to make sure if it is working fine or not. So I have created stub for that specific function and when I tries to calls fake that stub, the function is actually getting called instead of fake call. Below is my scenario:
I have an main function from where I'm calling the function saveData(**).
saveData(**) function is calling AWS SQS to save an message to DB
Below is my main function:
'use strict';
async function mainFunction() {
try {
await saveData(
name,
age,
);
return true;
} catch (e) {
console.log('Error - [%s]', e);
return null;
}
}
module.exports = { mainFunction };
Below is my saveData(**) function:
'use strict';
const AWS = require('aws-sdk');
const sqs = new AWS.SQS();
const saveData = async (
name,
age,
) => {
await sendMessage(JSON.stringify(dbData));
const params = {
DelaySeconds: <some_delay>,
MessageAttributes: <messageAttributes>,
MessageBody: {name:name, age:age},
QueueUrl: <URL_FOR_QUEUE>,
};
return sqs.sendMessage(params).promise();
return true;
};
module.exports = {
saveData,
};
And my test case is,
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
require('app-module-path').addPath('./src');
const sinon = require('sinon');
const app = express();
const sqsSender = require('lib/queue');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const main = require('../../../src/main-function');
const routes = require('routes');
routes.configure(app);
let mainFunctionStub;
let saveDataStub;
describe('/v1/main', () => {
beforeEach(() => {
mainFunctionStub = sinon.stub(main, 'mainFunction');
saveDataStub = sinon.stub(sqsSender, 'saveData');
});
describe('Test', () => {
it(`should return success`, (done) => {
const name = 'Name';
const age = 'Age';
saveDataStub.resolves({
name,
age,
});
});
});
afterEach(() => {
mainFunctionStub.restore();
mainFunctionStub.reset();
saveDataStub.restore();
saveDataStub.reset();
});
});
But this test is returning,
error: Jun-20-2021 20:07:05: Error - [Error [ConfigError]: Missing region in config
and,
Error: Timeout of 3500ms exceeded.
From this error I can say that this is actually calling SQS function instead of faking. How can I resolve this or how can I fake call to this function? I'm new to this unit testing so any help would be appriciated.
Stubbing works by replacing the property on the exports object. Since the require happens before sinon replaces the function, you capture the reference to the original function instead of dynamically using the currently set one.
You haven't showed your require calls in the main file, but from the call-site I infer you're importing it like const { saveData } = require('../foo/sqsSender'). This means you're grabbing the function off of the object when first loading the code. If you instead keep a reference to the sqsSender module instead, and reference the function on invocation, the stub should work.
'use strict';
// Don't destructure / grab the function.
const sqsSender = require("../foo/sqsSender")+
async function mainFunction() {
try {
// Use reference through module
await sqsSender.saveData(
name,
age,
);
return true;
} catch (e) {
console.log('Error - [%s]', e);
return null;
}
}
module.exports = { mainFunction };

How to mock npm module with sinon/mocha

I'm trying to test a function that calls the module cors. I want to test that cors would be called. For that, I'd have to stub/mock it.
Here is the function
cors.js
const cors = require("cors");
const setCors = () => cors({origin: 'http//localhost:3000'});
module.exports = { setCors }
My idea of testing such function would be something like
cors.test.js
describe("setCors", () => {
it("should call cors", () => {
sinon.stub(cors)
setCors();
expect(cors).to.have.been.calledOnce;
});
});
Any idea how to stub npm module?
You can use mock-require or proxyquire
Example with mock-require
const mock = require('mock-require')
const sinon = require('sinon')
describe("setCors", () => {
it("should call cors", () => {
const corsSpy = sinon.spy();
mock('cors', corsSpy);
// Here you might want to reRequire setCors since the dependancy cors is cached by require
// setCors = mock.reRequire('./setCors');
setCors();
expect(corsSpy).to.have.been.calledOnce;
// corsSpy.callCount should be 1 here
// Remove the mock
mock.stop('cors');
});
});
If you want you can define the mock on top of describe and reset the spy using corsSpy.reset() between each tests rather than mocking and stopping the mock for each tests.

simple ProxyRequire is not working?

i have been stuck with making Proxy-require work, below is my code and test file. I am trying to stub a function inside the code file using proxyRequire
//createSignature.js
'use strict';
var keys = require('../../../../utils/keys');
module.exports = function createSignature(transaction) {
try {
let prvkeyDecoded = keys.bs58_encode('test');
return true
} catch (err) {
}
};
here is the test file
//createSignature_unit.js
'use strict';
const sinonChai = require("sinon-chai");
const chai = require('chai');
chai.use(sinonChai);
const sinon = require('sinon');
const createSignature = require('./createSignature');
const proxyquire = require('proxyquire').noPreserveCache().noCallThru();
const keysMock =
{
bs58_encode: sinon.stub()
};
const test =
proxyquire('./createSignature', {
'../../../../utils/keys': keysMock
})
describe('test backend', () => {
it("Create Signature with stubs", function() {
test('test')
expect(keysMock.bs58_encode).to.have.been.calledOnce;
});
});
my test function is not called, and keysMock.bs58_encodealso is not been called even once. Am i missing something?
//output window
1) Create Signature with stubs
0 passing (9ms)
1 failing
1) test backend
Create Signature with stubs:
AssertionError: expected stub to have been called exactly once, but it was called 0 times
at Context.<anonymous> (createSignature_unit.js:37:46)
In addition to this if i just call
it("Create Signature with stubs", function() {
expect(test('fg')).to.be.true
//expect(keysMock.bs58_encode).to.have.been.calledOnce;
});
i get output as AssertionError: expected undefined to be true
Your stub wrong function as mock. In test, you stub bs58_encode but in the source file, you use bs58_decode. Change it into bs58_decode should fix it.
const keysMock = {
bs58_decode: sinon.stub() // change to decode
};
const test =
proxyquire('./createSignature', {
'../../../../utils/keys': keysMock
})
describe('test backend', () => {
it("Create Signature with stubs", function () {
test('test')
expect(keysMock.bs58_decode).to.have.been.calledOnce; // change to decode
});
});

Sinon mock is calling the real method

I want to mock a method on a class so that a dummy method is called which I can obtain stats on such as how many time it was invoked etc...
I'm trying to do it this way using Sinon, but the actual method is still invoked (and verify isn't registering the call)
I'm using Sinon with Jest ... yes I know Jest has it's own mocking /stubbing/spying capabilities but I've been having trouble with them in Node so I'm looking at Sinon. I can't use Mocha instead of jest which usually pairs with Sinon (sigh, corporate).
Test
const sinon = require('sinon')
const Cache = require('../../../adapters/Cache')
const Fizz = require('../Fizz')
describe('CACHE', () => {
it('should return a mock', () => {
const mockCache = sinon.mock(Cache.prototype, 'retrieveRecords')
const fizz = new Fizz()
fizz.doStuff()
mockCache.expects('retrieveRecords').once()
mockCache.verify()
})
})
Fizz
const Cache = require('../../adapters/Cache')
const Thing = require('../../adapters/Thing')
class Fizz {
doStuff() {
const thing = new Thing()
const cache = new Cache()
return cache.retrieveRecords('foo')
}
}
module.exports = Fizz
I got my syntax wrong. Needed to call mockCache.expects('retrieveRecords') to make it not call the real method, I was thinking this counted the invocations, but that's what I get from not RTFM. This works:
const sinon = require('sinon')
const Cache = require('../../../adapters/Cache')
const Fizz = require('../Fizz')
describe('CACHE', () => {
it('should return a mock', () => {
const mockCache = sinon.mock(Cache.prototype)
const expectation = mockCache.expects("retrieveRecords")
expectation.once()
const fizz = new Fizz()
const res = fizz.doStuff()
mockCache.verify()
})
})

Stub class instance method to return resolved Promise (using Sinon)

I am writing controller unit tests for a Node Express app.
The controller creates an instance of a model class and then calls one of its method that returns a resolved Promise. I need to stub the class constructor and then the method so that it returns a Promise resolved with test data.
Controller:
const Model = require('../../models/model');
module.exports = function (req, res, next) {
const instance = new Model(req.body);
instance.method()
.then(result => {
// do something with result
})
.catch(err => next(err));
};
Test:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
require('sinon-as-promised');
const Model = require('../../../../server/models/model');
const stubs = {
model: sinon.stub(Model.prototype, 'method', function () { sinon.stub().resolves('foobar') })
};
const subject = proxyquire('../../../../server/controllers/models/method', {
'../../models/model': stubs.model
});
Sinon.JS Documentation Stub API says:
var stub = sinon.stub(object, "method", func);
Replaces object.method with a func, wrapped in a spy.
But I get this error when the test code hits .then in the controller:
instance.method(...).then is not a function
Calling .resolves() (from sinon-as-promised) directly on the stub gives the then/catch/finally methods to the class instance rather than to the class instance method as required:
sinon.stub(Model.prototype, 'method').resolves('foobar')
Thanks in advance for help!
You need to return sinon.stub().resolves('foobar') from your stub function.
const stubs = {
model: sinon.stub(Model.prototype, 'method',
function () { return sinon.stub().resolves('foobar') })
};
But you'd probably just be better off returning a native Promise since you aren't keeping a reference to the inner stub:
const stubs = {
model: sinon.stub(Model.prototype, 'method',
function () { return Promise.resolve('foobar') })
};
Have discovered this solution where you make your own fake Model:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
require('sinon-as-promised');
const methodStub = sinon.stub().resolves('foobar');
const ModelStub = function () {
this.method = methodStub;
};
const subject = proxyquire('../../../../server/controllers/models/method', {
'../../models/model': ModelStub
});
Credit goes to Darren Hurley.

Resources