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
Related
I'm writing a unit test for a piece of code that loads a file from an AWS S3 bucket and process it. The processing is done by Papa.parse through createReadStream. I fear I may have to mock the interaction between the S3 file and Papa.parse.
My code:
const { reader, s3 } = require('../util');
file = await reader(
SOURCE_BUCKET_NAME,
SOURCE_BUCKET_PREFIX,
FILE_PREFIX,
FILE_SUFFIX,
);
const s3file = s3.getObject({ Bucket: SOURCE_BUCKET_NAME, Key: file.Key });
return new Promise((resolve, reject) => {
Papa.parse(s3file.createReadStream().pipe(zlib.createGunzip()), {
encoding: 'utf8',
header: true,
step: (line) => {
const d = line.data[0];
// handling irrelevant to the mock issue
},
complete: async () => {
// more handling
},
});
});
reader() is a utility function that wraps some checks and s3 request and returns the file we want to load. s3 is the actual AWS s3 object that's been instantiated by the imported utility.
In my tests, I don't want to use the real s3 at all, so I want to mock both the reader() function and the s3 object, of which I'm only calling s3.getObject.
So this is how I'm mocking this:
const util = require('../util');
describe('blah', () => {
beforeEach(() => {
jest.mock('../util', () => jest.fn());
const mockReadStream = jest.fn().mockImplementation(() => {
const readable = new Readable();
readable.push('fieldA, fieldB\n');
readable.push('value A1, value B1\n');
readable.push('value A2, value B2\n');
readable.push(null);
return readable;
});
s3GetObject = jest.fn(() => ({
createReadStream: fn(() => ({
pipe: mockReadStream,
})),
}));
util.reader = jest.fn((bucketName, bucketPrefix, filePrefix, fileSuffix) => ({
Key: `/${filePrefix}__20201021.${fileSuffix}`,
}));
util.s3 = jest.fn(() => ({
getObject: s3GetObject,
}));
});
});
As far as I can find online, this should work, but it doesn't. The unit code loads the actual file from the real S3 bucket, and not my mock.
Thing is, I'm using this same way of mocking (const {x} = require(y) and in the test y.x = jest.fn(), and there it works fine. Although I've also used it somewhere where it didn't work if I mocked one import, but it did work if I mocked a secondary import that the first import depended upon. I have no idea why, but my workaround worked, so I didn't worry about it. But this time it doesn't work at all, and really don't want to secondary dependency, because then I'd have to mock the entire S3 interface. (The S3 interface I'm importing here is a simple wrapper.)
I found the solution myself: manual mocks.
Create a __mocks__ folder next to the file I want to mock, put in it a file with the same name, and these contents:
const { Readable } = require('stream');
const mockReadStream = jest.fn().mockImplementation(() => {
const readable = new Readable();
readable.push('fieldA, fieldB\n');
readable.push('value A1, value B1\n');
readable.push('value A2, value B2\n');
readable.push(null);
return readable;
});
const s3GetObject = () => ({
createReadStream: () => ({
pipe: mockReadStream,
}),
});
const s3 = {
getObject: s3GetObject,
};
const reader = async (bucketName, dirPrefix = '/', filePrefix, fileSuffix) => ({
Key: `/${filePrefix}__20201021_2020102112345.${fileSuffix}`,
});
module.exports = {
reader,
s3,
};
Then in the unit test file, start with:
jest.mock('../../datamigrations/util');
Remove all the other mocking code and the original require. Now jest will load the mocked util instead of the real util.
Primary downside: I can't check how often various methods have been called, but for my specific case, that's not a problem. (Because I'm also mocking access to the database I'm pushing this data to, and I can still pass that mock a jest.fn()).
I would like to write some unit tests for feathers services.
I want this test to run completely independent, which means i do not want to use the database.
This is an example snippet of my service which is using sequelize:
src/services/messages/messages.service.js
// Initializes the `messages` service on path `/messages`
const createService = require('feathers-sequelize');
const createModel = require('../../models/messages.model');
const hooks = require('./messages.hooks');
const filters = require('./messages.filter');
module.exports = function (app) {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'messages',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/messages', createService(options));
// Get our initialized service so that we can register hooks
const service = app.service('messages');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
I would maybe try to mock the database with the library sequelize-test-helpers but I am not sure how this would work in combination with feathers.
This is how my current test in typescript for this service looks like:
src/test/services/messages.test.ts
import assert from 'assert';
import { app } from '../../src/app';
describe('\'messages\' service', () => {
before(() => {
// maybe add an entry to the mocked database
});
after(() => {
// maybe delete that entry
});
it('registered the service', () => {
const service = app.service('messages');
assert.ok(service, 'Registered the service');
});
it('returns a single record', async () => {
// get result with id 1 (maybe added item in before-hook)
const res = await service.get(1);
should().exist(res);
res.should.be.a('object');
// more checks...
});
});
The first 'it(...)' was generated by feathers itself and the second 'it(...)' shows the functionality I want the test to have.
But the problem is that I am not sure how to write this test so that the service will not use the original database.
Does anybody of you have an idea how I could write a test for a feathers service without using the actual database?
Thanks in advance!
Set environment to TEST and in config set the database on the test.json . As seen here : https://docs.feathersjs.com/guides/basics/testing.html#test-database-setup
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.
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()
})
})