How to mock an external module's function with Jest - jestjs

Can Jest's mocking handle functions from modules I didn't write?
node-yelp-api-v3 has Yelp.searchBusiness(String) but my attempts to use Jest's mocking functionality are unsuccessful. The Jest examples seem to assume that I'm mocking a module I have in the project. From the documentation I'm also unclear how to mock a specific function in a module.
Neither of these are working:
jest.mock('Yelp.searchBusiness', () => {
return jest.fn(() => [{<stubbed_json>}])
})
or
jest.mock('Yelp', () => {
return jest.fn(() => [{<stubbed_json>}])
})
I'm currently using sinon but would like to use just Jest. This Sinon approach works:
var chai = require('chai')
var should = chai.should()
var agent = require('supertest').agent(require('../../app'))
const Yelp = require('node-yelp-api-v3')
var sinon = require('sinon')
var sandbox
describe('router', function(){
beforeEach(function(){
sandbox = sinon.sandbox.create()
stub = sandbox.stub(Yelp.prototype, 'searchBusiness')
})
afterEach(function(){
sandbox.restore()
})
it ('should render index at /', (done) => {
/* this get invokes Yelp.searchBusiness */
agent
.get('/')
.end(function(err, res) {
res.status.should.equal(200)
res.text.should.contain('open_gyro_outline_500.jpeg')
done()
})
})
})

Mocking external modules is explained here.
If the module you are mocking is a Node module (e.g.: lodash), the mock should be placed in the __mocks__ directory adjacent to node_modules (unless you configured roots to point to a folder other than the project root) and will be automatically mocked. There's no need to explicitly call jest.mock('module_name').
For your exact case this would mean you need to create a folder __mocks__ with a file node-yelp-api-v3.js in it. In that file you create a mock object from the original module using genMockFromModule and override the method you want to mock.
// __mocks__/node-yelp-api-v3.js
const yelp = jest.genMockFromModule('node-yelp-api-v3')
function searchBusiness() {
return [{<stubbed_json>}]
}
yelp.searchBusiness = searchBusiness
module.exports = yelp
Additionally you could also wrap the searchBusiness in jest.fn if you want to call assertions like searchBusiness.mock.calls.length for this method later.

You can also do this:
jest.mock('Yelp', () => ({
searchBusiness: jest.fn(() => [{<stubbed_json>}])
})
And then you'll be able to call things like expect(Yelp.searchBusiness).toHaveBeenCalled() etc.

Related

How to share variable in before and after each run in jest?

In beforeEach I am seeding my db. There is ID (is created on server side). I need to clean my db in afterEach. How can I share this variable? Is safe to store in some global context?
describe('test', () => {
beforeEach(async () => {
const ID = axios.post(...)
});
afterEach(async()=> {
axios.delete(ID)
});
it('....', () => {
// test
});
});
Variables can be shared between tests by just placing then outside of the describe function scope as follows:
const ID = axios.post(...)
describe(...
I would additionally recommend to mock out the axios post and work with mock values exclusively as relying on outside services in unit tests is an anti-pattern.

Mocking axios all / spread

I've got a very basic component that uses axios.all to make 3 calls to a jokes api and then stores the values in state. I then map those values on the page. Very basic stuff.
I'm trying to write a test that mocks the axios.all that I can pass some hard coded responses to. I want to prove that the data binding is happening correctly after the call has resolved.
I'm having a very hard time doing this, and I was wondering if anyone had any insights.
Link to CodeSandbox
Thanks in advance for any and all help!
The problem with your test is that you are not mocking the axios methods. Simply calling it axiosMock when you import the library is not how it works.
You have to actually mock the methods you use:
describe("<TestScreen />", () => {
afterEach(() => {
cleanup();
jest.clearAllMocks();
});
beforeEach(() => {
// Mock all the calls to axios.get, each one returning a response.
axios.get = jest
.fn()
.mockResolvedValueOnce(RESPONSES[0])
.mockResolvedValueOnce(RESPONSES[1])
.mockResolvedValueOnce(RESPONSES[2]);
// Spy on spread method so that we can wait for it to be called.
jest.spyOn(axios, "spread");
});
test("placeholder", async () => {
const { queryAllByTestId } = render(<App />);
await waitFor(() => expect(axios.spread).toHaveBeenCalledTimes(1));
const setups = queryAllByTestId("setup");
const punchlines = queryAllByTestId("punchline");
expect(setups.length).toBe(3);
expect(punchlines.length).toBe(3);
});
});

Testing functions with properties using Jest and Sinon

am trying to write tests to test streams on my app, dealing with fs library with its createWriteStream function, so the stub i created is as follows:
writeStreamStub = sinon.stub()
onceStreamEventStub = sinon.stub()
endStreamStub = sinon.stub()
onStreamStub = sinon.stub()
createStreamStub = sinon.stub(fs, 'createWriteStream').returns({
write: writeStreamStub,
once: onceStreamEventStub,
end: endStreamStub,
on: onStreamStub
})
So now I can test for whether the functions are called and the returned functions are also called. But I am using the --coverage flag and the code of the callbacks of the returned functions is not covered, the write method is called inside a process.nextTick and I have no idea how to go about this. Is it possible to cover the whole code and the code inside the callbacks, and if so, how do I go about it. Thanks in advance.
N.B. The variables are globaly declared
If there's no cogent reason to use both sinon and jest, I'd recommend just using one library. If you decide to go with jest, here's a simple example. Assume you have a class like
const fs = require('fs');
module.exports = class FileWriter {
constructor() {
this.writer = fs.createWriteStream('./testfile.txt');
}
writeFile() {
process.nextTick(() => {
this.writeContent('hello world');
});
}
writeContent(content) {
this.writer.write(content);
this.writer.end();
}
};
and in your unit-test you want to mock the behaviour of all the used fs-functions (createWriteStream, writer, end in this case) and just check if they are called with the correct arguments. You could do this with something like this:
const fs = require('fs');
const FileWriter = require('./FileWriter');
// use this to have mocks for all of fs' functions (you could use jest.fn() instead as well)
jest.mock('fs');
describe('FileWriter', () => {
it('should write file with correct args', async () => {
const writeStub = jest.fn().mockReturnValue(true);
const endStub = jest.fn().mockReturnValue(true);
const writeStreamStub = fs.createWriteStream.mockReturnValue({
write: writeStub,
end: endStub,
});
const fileWriter = new FileWriter();
fileWriter.writeFile();
await waitForNextTick();
expect(writeStreamStub).toBeCalledWith('./testfile.txt');
expect(writeStub).toBeCalledWith('hello world');
expect(endStub).toHaveBeenCalled();
});
});
function waitForNextTick() {
return new Promise(resolve => process.nextTick(resolve));
}

Sinon spy calledWithNew not working

I'm trying to use sinon or sinon-chai's calledWithNew (or simply called), but can't seem to get it to work, I've looked at a few suggestions online without luck either, here is the function I'm trying to test:
users.js
exports.create = function (data) {
//some validation
var user = new User(data);
return user.save().then((result) => {
return mailer.sendWelcomeEmail(data.email, data.name).then(() => {
return {
message: 'User created',
userId: result.id
};
});
}).catch((err) => {
return Promise.reject(err);
});
}
Here is my test:
users.test.js
beforeEach(() => {
saveStub = sandbox.stub(User.prototype, 'save').resolves(sampleUser);
spy = sandbox.spy(User);
});
afterEach(() => {
sandbox.restore();
});
it('should call user.save', async () => {
result = await users.create(sampleArgs);
expect(saveStub).to.have.been.called; //-> true
expect(spy).to.have.been.called; //-> false, calledWithNew returns same result as well
});
I found several posts suggesting spying on (window, 'className') but I'm using mocha, not a browser.
Trying to spy on (global, User / User.prototype) didn't work either.
User is a module-level variable in users.js. Sinon cannot affect it. When you do this in your test file:
spy = sandbox.spy(User);
You're creating a spy in the scope of your test file, sure, but your module is still using the original.
To do something like this, you need to export your constructor inside an object, then both invoke it and spy on it through that object:
Wherever User is coming from, let's say user.js:
class User {
// whatever your User implementation is
}
module.exports = { User };
users.js:
const userModule = require('./user');
...
var user = new userModule.User(data);
Then, in your test file:
const userModule = require('./user');
spy = sandbox.spy(userModule, 'User');
Another way to do this would be to use something like proxyquire. It can make these kinds of tests less obtrusive, but can make your tests more confusing to readers.
My preference is generally to keep constructors very simple so I don't ever have to spy on them. I've never used calledWithNew in any of my own projects for this reason. :\ It's up to you, though.

Mock a class method using mockery and sinon

I'm learning to unit test using the node module mockery with sinon.
Using only mockery and a plain class I'm able to inject a mock successfully. However I would like to inject a sinon stub instead of a plain class but I'm having a lot of troubles with this.
The class I am trying to mock:
function LdapAuth(options) {}
// The function that I want to mock.
LdapAuth.prototype.authenticate = function (username, password, callback) {}
And here is the code I'm currently using in my beforeEach() function:
beforeEach(function() {
ldapAuthMock = sinon.stub(LdapAuth.prototype, "authenticate", function(username, password, callback) {});
mockery.registerMock('ldapauth-fork', ldapAuthMock);
mockery.enable();
});
afterEach(function () {
ldapAuthMock.restore();
mockery.disable();
});
I've tried to mock/stub the LdapAuth class in various ways without success and the code above is just the latest version that doesn't work.
So I just want to know how to mock this successfully using sinon and mockery.
These node mocking libraries can be quite cumbersome because of Node's module cache, Javascript's dynamic nature and it's prototypical inheritance.
Fortunately Sinon also takes care of modifying the object you are trying to mock as well as providing a nice API to construct spys, subs and mocks.
Here is a small example of how I would stub the authenticate method:
// ldap-auth.js
function LdapAuth(options) {
}
LdapAuth.prototype.authenticate = function (username, password, callback) {
callback(null, 'original');
}
module.exports = LdapAuth;
// test.js
var sinon = require('sinon');
var assert = require('assert');
var LdapAuth = require('./ldap-auth');
describe('LdapAuth#authenticate(..)', function () {
beforeEach(function() {
this.authenticateStub = sinon
// Replace the authenticate function
.stub(LdapAuth.prototype, 'authenticate')
// Make it invoke the callback with (null, 'stub')
.yields(null, 'stub');
});
it('should invoke the stubbed function', function (done) {
var ldap = new LdapAuth();
ldap.authenticate('user', 'pass', function (error, value) {
assert.ifError(error);
// Make sure the "returned" value is from our stub function
assert.equal(value, 'stub');
// Call done because we are testing an asynchronous function
done();
});
// You can also use some of Sinon's functions to verify that the stub was in fact invoked
assert(this.authenticateStub.calledWith('user', 'pass'));
});
afterEach(function () {
this.authenticateStub.restore();
});
});
I hope this helps.

Resources