How can I mock a function called by the function I am exercising? The functions are in the same file [duplicate] - jestjs

I have one file called helper.js that consist of two functions
export const funcA = (key) => {
return funcB(key)
};
export const funcB = (key,prop) => {
return someObj;
};
I have my helper.spec.js to test the helper.js file functions.
import {funcA,funcB} from 'helper';
describe('helper', () => {
test('testFuncB', () => {
}
test('testFuncA', () => {
}
}
The test for funcB is pretty simple i just call it and expect someObj
The problem is to test funcA, in order to test it i want to mock the response of funcB.
I want testFuncB call the actual funcB and testFuncA call mocked funcB
How can i achieve funcB to be mocked and original in my two tests?
This is not a duplicate. It is a different case: they mock inner called functions only, if I remove the testFuncB then it will be the same but I must perform test on testFuncB too.

If an ES6 module directly exports two functions (not within a class, object, etc., just directly exports the functions like in the question) and one directly calls the other, then that call cannot be mocked.
In this case, funcB cannot be mocked within funcA the way the code is currently written.
A mock replaces the module export for funcB, but funcA doesn't call the module export for funcB, it just calls funcB directly.
Mocking funcB within funcA requires that funcA call the module export for funcB.
That can be done in one of two ways:
Move funcB to its own module
funcB.js
export const funcB = () => {
return 'original';
};
helper.js
import { funcB } from './funcB';
export const funcA = () => {
return funcB();
};
helper.spec.js
import * as funcBModule from './funcB';
import { funcA } from './helper';
describe('helper', () => {
test('test funcB', () => {
expect(funcBModule.funcB()).toBe('original'); // Success!
});
test('test funcA', () => {
const spy = jest.spyOn(funcBModule, 'funcB');
spy.mockReturnValue('mocked');
expect(funcA()).toBe('mocked'); // Success!
spy.mockRestore();
});
});
Import the module into itself
"ES6 modules support cyclic dependencies automatically" so it is perfectly valid to import a module into itself so that functions within the module can call the module export for other functions in the module:
helper.js
import * as helper from './helper';
export const funcA = () => {
return helper.funcB();
};
export const funcB = () => {
return 'original';
};
helper.spec.js
import * as helper from './helper';
describe('helper', () => {
test('test funcB', () => {
expect(helper.funcB()).toBe('original'); // Success!
});
test('test funcA', () => {
const spy = jest.spyOn(helper, 'funcB');
spy.mockReturnValue('mocked');
expect(helper.funcA()).toBe('mocked'); // Success!
spy.mockRestore();
});
});

Late answer but this should work.
Also you should test funcB in its own file and not inside the 'helper' tests.
import { funcB } from './funcB';
import { funcA } from './helper';
jest.mock('./funcB');
describe('helper', () => {
test('test funcA', () => {
const funcBSpy = jest.fn();
funcB.mockImplementation(() => funcBSpy());
funcA();
expect(funcBSpy).toHaveBeenCalledTimes(1);
});
});

import * as helper from 'helper';
describe('helper', () => {
it('should test testFuncA', () => {
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
mockTestFuncB.spyOn(helper, 'testFuncB').mockReturnValue(/*your expected return value*/);
// test logic
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
}
}

I create a kind of nameSpace to handle this issue:
let helper = {}
const funcA = (key) => {
return helper.funcB(key)
};
const funcB = (key,prop) => {
return someObj;
};
helper = { funcA, funcB }
module.exports = helper
and then mocking is obvious with jest.fn

You can use babel-plugin-rewire provided __set__ function to mock internal function.
Assuming you have set up babel-plugin-rewire.
helper.spec.js
import {funcA, __set__} as helper from './helper';
describe('helper', () => {
test('test funcA', () => {
__set__('funcB', () => {
return 'funcB return value'
})
expect(funcA()).toBe('funcB return value');
});
});
One advantage of this solution is you don't need to change any original code

I was able to get this working. I separated my helper and my main logic into two files like other solutions. In the test file, I had to mock the entire helper file.
const { doAdd } = require('./addHelper');
function add(a, b) {
return doAdd(a, b);
}
jest.mock('./addHelper');
// ...
it('should call doAdd', () => {
// hook into doAdd helper method and intercept its return value
jest.spyOn(helperModule, 'doAdd').mockReturnValue(11);
expect(addModule.add()).toEqual(11);
expect(helperModule.doAdd).toBeCalled();
});
Here is my solution:
https://github.com/davidholyko/jest-sandbox

You can do the following trick when you test the funcA:
1.Mock the funcB:
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
2.Change the funcB(key) to this.funcB(key)
I had the same problem and worked! Full Code:
export const funcA = (key) => {
return this.funcB(key)
};
export const funcB = (key,prop) => {
return someObj;
};
Test Code:
import helper from 'helper';
describe('helper', () => {
test('testFuncB', () => {
...
}
test('testFuncA', () => {
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
}
}

I think this might work
import * as helper from 'helper';
describe('helper', () => {
test('testFuncB', () => {
}
test('testFuncA', () => {
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
jest.spyOn(helper, 'testFuncB').mockImplementationOnce(mockTestFuncB);
// Do the testing ...
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
}
}

Related

How to mock function in service called by another method in the service called by the controller

I'm trying to test my controller function which is:
import { InstalledPackages } from '../parser/parser.service';
import {
getOutdatedPackages,
InstalledPackageStatus,
} from './version-control.service';
interface OutdatedPackages {
dependencies: InstalledPackageStatus[];
devDependencies: InstalledPackageStatus[];
}
export async function getPackagesUpdatesToNotify(
packages: InstalledPackages,
type = 'package.json',
): Promise<OutdatedPackages> {
return getOutdatedPackages(packages.dependencies, type);
}
And having my service like this:
import { fetch } from "../common/http.service";
export async function getLastPackageVersion(
packageName: string
): Promise<VersionType> {
const url = `https://registry.npmjs.org/-/package/${packageName}/dist-tags`;
return await (<Promise<VersionType>>fetch(url));
}
export async function getOutdatedPackages(
installedPackages: PackagesVersion,
type: string
): Promise<InstalledPackageStatus[]> {
return Promise.all(
Object.keys(installedPackages).map(async (packageName) =>
getLastPackageVersion(packageName)
)
);
}
I've already tried both solutions:
import * as myService from './my.service';
it('my test', async () => {
const getLastPackageVersionSpy = jest.spyOn(myService, 'getLastPackageVersion').mockReturnValue(
Promise.resolve(42),
await getPackagesUpdatesToNotify(packages, type)
});
and
import { getLastPackageVersion } from './my.service';
import { getPackagesUpdatesToNotify } from './version-control.controller';
jest.mock('./myse.service', () => ({
...jest.requireActual('./myse.service'),
getLastPackageVersion: jest.fn(),
}));
it('my test', async () => {
(getLastPackageVersion as jest.Mock).mockResolvedValue(
Promise.resolve(42),
);
await getPackagesUpdatesToNotify(packages, type)
});
But the original function is always called instead of the mocked one.
How to mock the getLastPackageVersion method.
I'm trying to avoid using tools like rewire if possible.
Thank you
Move the getLastPackageVersion to different file, import it in the my.service and then mock it.
my.service:
import { fetch } from "../common/http.service";
import { getLastPackageVersion } from '../last-package-version';
export async function getOutdatedPackages(
installedPackages: PackagesVersion,
type: string
): Promise<InstalledPackageStatus[]> {
return Promise.all(
Object.keys(installedPackages).map(async (packageName) =>
getLastPackageVersion(packageName)
)
);
}
import * as lastPackageVersion from '../last-package-version';
it('my test', async () => {
const getLastPackageVersionSpy = jest.spyOn(lastPackageVersion, 'getLastPackageVersion').mockResolvedValue(42);
await getPackagesUpdatesToNotify(packages, type)
});
getOutdatedPackages is in the same file as the getLastPackageVersion so it cannot be mocked. In your case the getOutdatedPackages is still using the original getLastPackageVersion method.

jest.spyOn not calling mocked implementation but rather actual function instead

I'm trying to write a unit test for a function that calls some helper functions in the same file. I'm using jest.spyOn to mock away those helper functions as it appears that it can do that.
myModule.js
export const getUserName = () => {
return "mjordan"
}
export const getItem = () => {
return 'basketball'
}
export const getUserWithItem = () => {
const userName = getUserName()
const item = getItem()
return userName + " " + item
}
myModule.test.js
import * as myModule from 'path/to/module'
describe('getUserWithItem', () => {
beforeEach(() => {
jest.restoreAllMocks()
})
it('Returns user with item', () => {
jest.spyOn(myModule, 'getUserName').mockImplementation(() => 'tigerwoods')
jest.spyOn(myModule, 'getItem').mockImplementation(() => 'golf ball')
const result = myModule.getUserWithItem()
expect(result).toEqual("tigerwoods golf ball")
})
})
However, the jest.spyOn function does not appear to be mocking the implementation of the spied on functions but the test is calling the original functions instead which results in an output of mjordan basketball instead.
Am I missing something with how spyOn is supposed to work?
The easiest way I have found to do what you want is to explicitly call the exported version of the function in your module, i.e.
export const getUserName = () => {
return "mjordan"
}
export const getItem = () => {
return 'basketball'
}
export const getUserWithItem = () => {
const userName = exports.getUserName()
const item = exports.getItem()
return userName + " " + item
}

Jest SpyOn __mocks__ module

We mock our modules using __mocks__. I'd like to spyOn a module function in my test but it doesn't seem to be working. Related question, I'd also like to override a mocked module function for a test (ie throw an exception). How can I do this?
├──__mocks__
| └──DB-Utils.js
| └──controllers
| └──myController.js
├──node_modules
__mocks__/DB-Utils.js:
const { MyController } = require('./controllers/myController');
module.exports = {
MyController,
};
__mocks__/controllers/myController.js:
class MyController {
async setAvailability(id, availability) {
return true;
}
}
module.exports = {
MyController,
};
test.spec.js:
const { MyController } = require('DB-Utils');
const myController = new MyController();
describe('Register Tests', () => {
fit('myController setAvailability', async () => {
---code that calls a class that ends up calling myController.setAvailability---
expect(myController.setAvailability).toHaveBeenCalledWith('foo', 'bar');
});
});
My tests pass in that the mock myController is called, however it fails the toHaveBeenCalledWith with an error of Number of calls: 0
How can I spy setAvailability?
For the related question I'd also like to be able to do something like the following:
describe('Register Tests', () => {
fit('myController setAvailability throws', async () => {
jest.spyOn(myController, 'setAvailability').mockImplementation(() => {
throw new Error()
});
expect(---code that calls a class that ends up calling myController.setAvailability---).toThrow();
});
});

Is it possible to mock local functions using Jest

I have a file (file.js) consisting of 3 functions in the same file:
file.js:
const fn1 = (number) => {
return number + 1;
}
export const fn2 = (value) => {
return value;
}
export const fn3 = (number) => {
return fn1(number);
}
export const fn4 = (number) => {
return fn2(number);
}
Is it possible to mock out these functions periodically using JEST? What I hope to achieve is something along these lines:
file.test.js:
import { fn2, fn3, fn4 } from 'file.js';
describe('fn2', () => {
test('normal test.' => {
expect(fn2(1)).toBe(1);
})
});
describe('fn3', () => {
test('mock fn1 for this test.' => {
mock fn1 = () => { return 1 };
expect(fn3(2)).toBe(1);
})
});
describe('fn4', () => {
test('mock fn2 for this test.' => {
mock fn2 = () => { return 1 };
expect(fn4(2)).toBe(1);
})
});
It is not possible through the way how JS modules work. Only exported functions are visible to the outside, all other stuff is only visible in the scope of the module, so there is no way to mock it. Also there is no need to mock this function as you only want to test the public API.
The only way to test the function itself and mock it for this module is to put it into an own module, which makes sense if it is some really complicated function where you need good test coverage and it is to complicated to get this when its in another module.

Mock.mockImplementation() not working

I have a service class
Service.js
class Service {
}
export default new Service();
And I am trying to provide a mock implementation for this. If I use something like this:
jest.mock('./Service', () => { ... my mock stuff });
It works fine, however I'm not able to access any variables declared outside of the mock, which is a bit limiting as I'd like to reconfigure what the mock returns, etc.
I tried this (inspired by this other StackOverflow article: Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error)
import service from './Service';
jest.mock('./Service', () => jest.fn);
service.mockImplementation(() => {
return { ... mock stuff }
);
Unfortunately when I am trying to run this, I get the below error:
TypeError: _Service2.default.mockImplementation is not a function
I had same problem as #Janos, the other answers didn't help either. You could do two things :
If you need to mock only a function from Service, in your test file:
import service from './Service';
jest.mock('./Service', () => jest.fn());
service.yourFunction = jest.fn(() => { /*your mock*/ })
 
If you need to mock the entire Module:
Say your service.js is in javascript/utils, create a javascript/utils/_mocks_ and inside it create a service.js file, you can then mock the entire class in this file, eg:
const myObj = {foo: "bar"}
const myFunction1 = jest.fn(() => { return Promise.resolve(myObj) })
const myFunction2 = ...
module.exports = {
myFunction1,
myFunction2
}
then in your test file you just add:
jest.mock('./javascript/utils/service')
...functions exported from the mockfile will be then hit through your test file execution.
The mock is equal to jest.fn. You need to call jest.fn to create a mocked function.
So this:
jest.mock('./Service', () => jest.fn);
Should be:
jest.mock('./Service', () => jest.fn());
ran into similar issues and resolved it by using .mockImplementationOnce
jest.mock('./Service', () => jest.fn()
.mockImplementationOnce(() => {
return { ... mock stuff }
})
.mockImplementationOnce(() => {
return { ... mock other stuff }
})
);
now when you run another test it will return the second mock object.
You need to store your mocked component in a variable with a name prefixed by "mock" and make sure you return an object with a default property as you import your Service from the default in your "main.js" file.
// Service.js
class Service {
}
export default new Service();
// main.test.js (main.js contains "import Service from './Service';")
const mockService = () => jest.fn();
jest.mock('./Service', () => {
return {
default: mockService
}
});
I had similar problem, and the cause was that ".spec.js" file had an
import jest from "jest-mock";
After removing this line, it worked.
My mistake was that I was resetting the mock before each test. If you do that, be sure to reconfigure the mock implementation.
For example, change this:
let value;
let onPropertyChange: OnPropertyChangeCallback = jest.fn((changes: any) => {
value = changes["testValue"];
});
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
});
to this:
let value;
let onPropertyChange: OnPropertyChangeCallback;
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
onPropertyChange = jest.fn((changes: any) => {
value = changes["testValue"];
});
});

Resources