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.
Related
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 };
I'm having trouble trying to mock a module with a constructor
// code.js
const ServiceClass = require('service-library');
const serviceInstance = new ServiceClass('some param');
exports.myFunction = () => {
serviceInstance.doSomething();
};
And the test code:
// code.test.js
const ServiceClass = require('service-library');
jest.mock('service-library');
const {myFunction} = require('../path/to/my/code');
test('check that the service does something', () => {
// ????
});
It's not like the Documentation example Mocking Modules because you need to instantiate the module after importing it. And isn't either like Mocking a Function.
How could I mock this doSomething() function while testing?
For reference, I'm trying to mock #google-cloud/* packages here. And I have a few projects that could take advantage on this.
You need to mock the whole module first so that returns a jest mock. Then import into your test and set the mock to a function that returns an object holding the spy for doSomething. For the test there is difference between of a class called with new and a function called with new.
import ServiceLibrary from 'service-library'
jest.mock( 'service-library', () => jest.fn())
const doSomething = jest.fn()
ServiceLibrary.mockImplementation(() => ({doSomething}))
Following #andreas-köberle solution I was able to mock #google-cloud/bigquery like so:
// mock bigquery library
const BigQuery = require('#google-cloud/bigquery');
jest.mock('#google-cloud/bigquery', () => jest.fn());
const load = jest.fn(() => ({'#type': 'bigquery#load_job'}));
const table = jest.fn(() => ({load}));
const dataset = jest.fn(() => ({table}));
BigQuery.mockImplementation(() => ({dataset}));
// mock cloud storage library
const {Storage} = require('#google-cloud/storage');
jest.mock('#google-cloud/storage');
const file = jest.fn(name => ({'#type': 'storage#file', name}));
const bucket = jest.fn(() => ({file}));
Storage.mockImplementation(() => ({bucket}));
I'm leaving this here as a reference in case someone else googles something similar. But to make it clear, thats just a particularization for #andreas-köberle answer
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
});
});
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);
});
});
});
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()
})
})