Unit testing an inner function? - javascript

I have a function that has inner functions, for my unit test, I only want to test the functionality of the inner function, but when I export the function and call the inner function, npm tests returns an error.
In my main.js:
mainFunction = () => {
functionToBeTested = () => {
// some code
}
}
module.exports = {mainFunction: mainFunction}
In my test.js
const chai = require("chai");
const assert = require("chai").assert;
const mainFunction = require("./main");
describe ("test", () => {
it("returns results", () => {
let result = mainfunction.functionToBeTested(args);
//equal code
});
})
But when I run npm test, it says:
mainfunction.functionToBeTested is not a function.
What am I doing wrong?

If you want to chain your functions you can try something like that.
main.js
const mainFunction = () => {
const functionToBeTested = () => {
return "I got it";
}
return { functionToBeTested };
}
module.exports = { mainFunction };
test.js
const chai = require("chai");
const assert = require("chai").assert;
const mainFunction = require("./main");
const mf = mainFunction();
describe ("test", () => {
it("returns results", () => {
let result = mf.functionToBeTested(args);
//equal code
});
});

Actually, you can't call a function declare inside another function that way. A solution would be to declare functionToBeTested outside mainFunction, then call it :
main.js
const functionToBeTested = () => {
// some code
};
const mainFunction = () => {
functionToBeTested();
};
module.exports = { mainFunction, functionToBeTested }
test.js
const chai = require("chai");
const assert = require("chai").assert;
const { mainFunction, functionToBeTested } = require("./main");
describe ("test", () => {
it("tests mainFunction", () => {
let main = mainfunction(args);
...
});
it("tests functionToBeTested"), () => {
let tested = functionToBeTested(args);
...
});
})

It is because only mainFunction() is exported and not the functionToBeTested(), outside this module JS doesn't knows about the existence of the functionToBeTested().
I will recommend you to move functionToBeTested separated and export that as well or have a helper method for calling it.

Related

Using Jest with NodeJS, Function in imported module not being mocked without using 'this' in main

In the setup below, if I run the test as is, myFunc is not mocked when I debug into handler.
However, if instead I add this. in front of the myFunc call in handler, then the function is mocked and everything works as expected.
Can someone please explain why this is? I'm new to mocking and can't see it.
I know what this does, but why won't jest mock without it since I told it to mock that function in the module?
index.js
const aws = require('aws-sdk')
exports.handler = async function (event, context) {
let s;
switch (event.func) {
case "myFunc":
console.log('Executing myFunc');
//making the call: s = await this.myFunc.apply(null, [event.params]) will make the mock work.
s = await myFunc.apply(null, [event.params])
console.log(s);
return s;
/*cases...*/
default:
// default behaviour
}
async myFunc({p1, p2}){
/* do something */
return x
}
exports.myFunc = myFunc
}
index.spec.js
jest.mock('./index.js', () => {
const allAutoMocked = jest.createMockFromModule('./index.js')
const actual = jest.requireActual('./index.js')
return {
__esModules: true,
...allAutoMocked,
myFunc : jest.fn().mockImplementation(() => ({ mockedValue: 'test' })),
handler: actual.handler
}
})
let index = require("./index.js")
describe('Test myFunc', () => {
test('If myFunc function was called', async () => {
var event = { func: 'myFunc', params: { p1: xx, p2: false } };
const context = {};
const logMock = jest.fn((...args) => console.log(...args));
const data = await handler(event, context);
})
})

How do I implement a unit test for a function that invokes two other functions

Inside this module, I want to test the getAppts function, which invokes two function. What is the correct way of evaluating the code that getAppts encompasses? Do I have to run db.getDatabase() and fetchAppts() as stubs inside the unit test function? My unit test implementation is, at best, incomplete, but it could be totally wrong.
'use strict'
const db = require('../db/db')
const log = require('../utils/logging')(__filename)
const { fetchAppts, fetchApptDetails } = require('../db/dao/appointment-dao')
async function getAppts (queryParams) {
log.debug('getAppts(): %j', queryParams)
const knex = db.getDatabase()
const appointments = await fetchAppts(queryParams, knex)
return appointments
}
async function getApptDetails (demoApptId) {
log.debug('getApptDetails(): %j', demoApptId)
const knex = db.getDatabase()
const apptDetails = await fetchApptDetails(demoApptId, knex)
return apptDetails
}
module.exports = {
getAppts,
getApptDetails
}
unit test setup:
'use strict'
const sinon = require('sinon')
const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const dirtyChai = require('dirty-chai')
const sinonChai = require('sinon-chai')
const { expect } = require('chai')
const proxyquire = require('proxyquire')
const apptsResponse = require('../../data/get-appts-response.json')
chai.use(dirtyChai)
chai.use(sinonChai)
chai.use(chaiAsPromised)
chai.should()
describe.only('appointment-service.js', () => {
let apptService
let apptDaoStub
let dbStub
beforeEach(() => {
dbStub = {
getDatabase: sinon.stub()
}
apptDaoStub = {
fetchAppts: sinon.stub()
}
apptService = proxyquire('../../../services/appointment-service', {
'../db/db': dbStub,
'../db/dao/appointment-dao': apptDaoStub
})
})
afterEach(() => sinon.restore())
describe('getAppts', () => {
it.only('should get appointments', async () => {
const appts = await apptService.getAppts({})
dbStub.getDatabase.should.have.been.calledTwice
})
})
})
You can use mock functions, you will need to import the module:
it('should get appointments', async () => {
const mockFn = jest.spyOn(db, 'getDatabase');
const appts = await apptService.getAppts({})
expect(mockFn).toBeCalledTimes(1); // depends on how many times fn is called
});
You can learn more about mocking here:
https://www.chakshunyu.com/blog/how-to-mock-only-one-function-from-a-module-in-jest/

Jest mock not allowing inner function to be resolved in individual test

I'm testing a cloud function named myCloudFn in my functions/send.js module. My tests are in functions/test/send.test.js:
// send.js
const { getCompareDate } = require('../utils.js');
async function myCloudFn(myTestDate) {
const compareDate = await getCompareDate(argToTest);
const isOlder = myTestDate < compareDate;
return isOlder ? 'older' : 'newer';
}
module.exports = { myCloudFn };
// send.test.js
const send = require('../send.js');
jest.mock('../utils', () => ({
getCompareDate: jest.fn(() => new Date('2020-01-31')) // default
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02')),
}));
describe('send.js', () => {
it('returns date comparison from myCloudFn()', async () => {
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
});
The test functions correctly and passes as expected when I mock getCompareDate in this way, but for flexibility, I would rather provide custom input values for getCompareDate inside my tests and not 'globally'. Here's what I've tried:
const mockGetCompareDate = jest.fn();
jest.mock('../utils', () => ({
getCompareDate: mockGetCompareDate,
}));
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
This method, however, is not working and throws an error:
ReferenceError: Cannot access 'mockGetCompareDate' before initialization
I've used this method with other tests as noted in the solution in this question, but I am not seeing similar results here. What am I missing?
Jest is hoisting the mocked function to the top of the module, and hence throws this error. The mock should instead be used right before you run the test. Further reading.
Try this:
const { getCompareDate } = require('../utils.js');
const mockGetCompareDate = jest.fn(() => new Date('2020-01-31'));
jest.mock('../utils.js', () => ({
__esModule: true,
getCompareDate: jest.fn(),
default: jest.fn()
}));
beforeAll(() => {
getCompareDate.mockImplementation(mockGetCompareDate);
});
To provide custom values do as you did before, when initialising the mock function. Source
Like this:
const mockGetCompareDate = jest.fn()
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
Or do as you did before inside the test. Source
Like this:
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));

How to check if one function triggers another function with `Mocha`, `Chai`, `Sinon`

How to create test with Mocha, Chai, Sinon to check if one function triggers another function.
I would like to check if funcToTrigger triggers funcToSpy
import { expect } from 'chai';
import sinon from 'sinon';
it('one function should trigger other function', () => {
const funcToSpy = () => {
console.log('I should be called');
};
const funcToTrigger = () => {
funcToSpy();
};
const spyFunc = sinon.spy(funcToSpy);
funcToTrigger();
expect(spyFunc.called).to.be.true;
});
When I test only one function it works fine:
it('function should be called', () => {
const funcToSpy = () => {
console.log('I should be called');
};
const spyFunc = sinon.spy(funcToSpy);
spyFunc();
expect(spyFunc.called).to.be.true;
});
Based on documentation:
var spy = sinon.spy(myFunc);
Wraps the function in a spy. You can pass this spy where the original
function would otherwise be passed when you need to verify how the
function is being used.
Usage examples:
import { expect } from 'chai';
import sinon from 'sinon';
it('use Object', () => {
const Test = {
funcToSpy: () => {
console.log('I should be called');
},
};
const funcToTrigger = () => {
Test.funcToSpy();
};
const spyFunc = sinon.spy(Test, 'funcToSpy');
funcToTrigger();
expect(spyFunc.called).to.be.true;
});
it('use Function', () => {
const funcToSpy = () => {
console.log('I should be called');
};
const spyFunc = sinon.spy(funcToSpy);
const funcToTrigger = () => {
spyFunc();
};
funcToTrigger();
expect(spyFunc.called).to.be.true;
});
it('use Function Argument', () => {
const funcToSpy = () => {
console.log('I should be called');
};
const funcToTrigger = (funcToSpy) => {
funcToSpy();
};
const spyFunc = sinon.spy(funcToSpy);
funcToTrigger(spyFunc);
expect(spyFunc.called).to.be.true;
});
Result:
$ npx mocha index.spec.js
I should be called
✓ use Object
I should be called
✓ use Function
I should be called
✓ use Function Argument
3 passing (3ms)
$
Your test fail because: funcToTrigger has defined and always calls the original funcToSpy.
In the 'use Object' case, funcToTrigger calls method inside object Test, which has been replaced by spy, which is wrapping funcToSpy.
In the 'use Function' case, funcToTrigger calls spy directly, and the spy is wrapping funcToSpy.
In the 'use Function Argument' case, funcToTrigger calls first argument which is a spy, which is wrapping funcToSpy.

How to mock a callback function from bookshelf js using sinnon

I would like to mock this piece of code that is using bookshelf js (with knex) with sinon.
const campaigns = await models.Campaign.forge()
.query((qb) => {
qb.where("account_id", accountId);
qb.andWhere("status", models.Campaign.STATUS.ACTIVE);
qb.andWhere(
"audience_create_trigger",
models.Campaign.AUDIENCE_CREATE_TRIGGER.ON_ENTER
);
})
.fetchAll();
How could I mock the inner queries inside the .query function.
I am a bit lost
Thank you very much!
I finally solved the problem using Sinon. This code is covering almost all the behavior
const assert = require("chai").assert
const sinon = require("sinon")
const models = require("../../../../models")
const query = {
query(func) {}
}
const qb = {
where(arg1, arg2) {},
andWhere(arg1, arg2) {}
}
const fetchAll = { async fetchAll() {} }
const forgeStub = sinon.stub(models.Campaign, "forge").returns(query)
const qbWhereStub = sinon
.stub(qb, "where")
.withArgs("account_id", accountId)
.returns(null)
const qbAndWhereStub = sinon.stub(qb, "andWhere").returns(null)
const queryStub = sandbox
.stub(query, "query")
.callsArgWith(0, qb)
.returns(fetchAll)
const fetchAllStub = sandbox.stub(fetchAll, "fetchAll").returns(campaigs)
//Calling the method
//Verify
assert.equal(qbWhereStub.callCount, 1)
assert.equal(qbAndWhereStub.callCount, 2)
assert.equal(forgeStub.callCount, 1)
assert.equal(queryStub.callCount, 1)
assert.equal(fetchAllStub.callCount, 1)
assert.isTrue(qbWhereStub.getCall(0).calledWithExactly("account_id", accountId))
assert.isTrue(qbAndWhereStub.getCall(0).calledWithExactly("status", models.Campaign.STATUS.ACTIVE))
assert.isTrue(
qbAndWhereStub
.getCall(1)
.calledWithExactly("audience_create_trigger", models.Campaign.AUDIENCE_CREATE_TRIGGER.ON_ENTER)
)
Try to use knex-mock-client to setup a test friendly knex instance.
The test will look much simpler.
import knex from "knex";
import { MockClient, getTracker } from "knex-mock-client";
describe("test", () => {
let db;
let tracker;
beforeAll(() => {
db = knex({ client: MockClient });
tracker = getTracker();
});
afterEach(() => tracker.reset());
it("should do something", () => {
tracker.on
.select(
(query) =>
query.sql.includes("table_name") && query.bindings.includes(accountId)
)
.responseOnce([]);
// execute query using db;
expect(tracker.history.select).toHaveLength(1);
});
});

Categories