You can mock the module like this:
import calculate from '../../calculate'
jest.mock('../xyz', ()=> () => Promise.resolve('mocked value'))
it('does something', async()=>{
const op = await calculate()
expect(op).toBe('mocked value')
})
if you need different return values from your mock you need to mock the module so it returns a spy. Then you have to import the module and you can set the return value during your tests:
import calculate from '../../calculate'
import myModule from '../xyz'
jest.mock('../xyz', ()=> jest.fn())
it('does something', async() => {
myModule.mockImplementation(() => () => Promise.resolve('mocked value'))
const op = calculate()
expect(op).toBe('mocked value')
})
it('does something else', async() => {
myModule.mockImplementation(() => () => Promise.resolve('another value'))
const op = await calculate()
expect(op).toBe('another value')
})
it('does fail', async() => {
myModule.mockImplementation(() => () => Promise.reject('some Error')
try{
const op = await calculate()
}catch (e){
expect(e).toBe('some Error')
}
})
Answer from Andreas Köberle on Stack OverflowYou can mock the module like this:
import calculate from '../../calculate'
jest.mock('../xyz', ()=> () => Promise.resolve('mocked value'))
it('does something', async()=>{
const op = await calculate()
expect(op).toBe('mocked value')
})
if you need different return values from your mock you need to mock the module so it returns a spy. Then you have to import the module and you can set the return value during your tests:
import calculate from '../../calculate'
import myModule from '../xyz'
jest.mock('../xyz', ()=> jest.fn())
it('does something', async() => {
myModule.mockImplementation(() => () => Promise.resolve('mocked value'))
const op = calculate()
expect(op).toBe('mocked value')
})
it('does something else', async() => {
myModule.mockImplementation(() => () => Promise.resolve('another value'))
const op = await calculate()
expect(op).toBe('another value')
})
it('does fail', async() => {
myModule.mockImplementation(() => () => Promise.reject('some Error')
try{
const op = await calculate()
}catch (e){
expect(e).toBe('some Error')
}
})
There's no need to mock, which is something of a broad sword, since it works at the module level. Jest's spyOn is a more focussed way of doing this, since it is at the method level.
The confusion here is solely because of the internal workings of 'xyz'. You have assumed that the module called 'xyz' has a function within it also called 'xyz'. Hence Jest fails to find it.
If we assume that 'xyz' is actually the default export of the 'xyz' module, we can test using this:
const mock = jest.spyOn(myModule, 'default')
All of the rest of your test code is perfect:
mock.mockReturnValue('mocked value')
const op = calculate()
expect(op).toBe('mocked value')
Jest: How to spy on an imported function
How to spy same module function in jest?
Cannot spy on individual functions that are individually exported
ecmascript 6 - Spying on an imported function that calls another function in Jest - Stack Overflow
You can mock the module like this:
import calculate from '../../calculate'
jest.mock('../xyz', ()=> () => Promise.resolve('mocked value'))
it('does something', async()=>{
const op = await calculate()
expect(op).toBe('mocked value')
})
if you need different return values from your mock you need to mock the module so it returns a spy. Then you have to import the module and you can set the return value during your tests:
import calculate from '../../calculate'
import myModule from '../xyz'
jest.mock('../xyz', ()=> jest.fn())
it('does something', async() => {
myModule.mockImplementation(() => () => Promise.resolve('mocked value'))
const op = calculate()
expect(op).toBe('mocked value')
})
it('does something else', async() => {
myModule.mockImplementation(() => () => Promise.resolve('another value'))
const op = await calculate()
expect(op).toBe('another value')
})
it('does fail', async() => {
myModule.mockImplementation(() => () => Promise.reject('some Error')
try{
const op = await calculate()
}catch (e){
expect(e).toBe('some Error')
}
})
There's no need to mock, which is something of a broad sword, since it works at the module level. Jest's spyOn is a more focussed way of doing this, since it is at the method level.
The confusion here is solely because of the internal workings of 'xyz'. You have assumed that the module called 'xyz' has a function within it also called 'xyz'. Hence Jest fails to find it.
If we assume that 'xyz' is actually the default export of the 'xyz' module, we can test using this:
const mock = jest.spyOn(myModule, 'default')
All of the rest of your test code is perfect:
mock.mockReturnValue('mocked value')
const op = calculate()
expect(op).toBe('mocked value')
Ideally you should test function independent. One test is not responsible for another function test functionality.
Not a best approach but you could do something like this:
util.js
export function add(a, b) {
return a + b;
}
export function showMessage(a, b, addNew = add) {
let sum = addNew(a, b);
return `The sum is ${sum}`;
}
And in your test you could do like this:
util.test.js
import * as Logics from "./util";
describe("showMessage", () => {
it("should return message with sum", () => {
let addSpy = jest.spyOn(Logics, "add");
addSpy.mockReturnValue(123);
let showMessageResponse = Logics.showMessage(2, 2, addSpy);
expect(addSpy).toHaveBeenCalledTimes(1);
expect(showMessageResponse).toBe(`The sum is 123`);
});
});
Here is the sandbox you can play with: https://codesandbox.io/s/jest-test-forked-9zk1s4?file=/util.test.js:0-366
On short you can't really achieve it without changing your exports at all. You can read the reasons (and maybe other options) on this answer as well as this answer.
A better option imo would be something like (in your logics.js file):
import * as Logics from './logics;
and in showMessage function use it like you did in your second example:
const sum = Logics.add(a, b)
Basically just importing everything in your logics.js and using that value in order to get the reference to the same add function.
Additional explanation: While you are mocking correctly the add, that is not the same function as the one called in showMessage and you basically can't mock that function (You can also check this code for proof
describe("showMessage", () => {
it("should return the mocked sum (PASSES)", () => {
jest.spyOn(Logics, "add").mockReturnValue(123);
const showMessageResponse = Logics.add(2, 2);
expect(showMessageResponse).toBe(123);
});
it("should return message with sum (FAILS)", () => {
let addSpy = jest.spyOn(Logics, "add").mockReturnValue(123);
const showMessageResponse = Logics.showMessage(2, 2);
expect(addSpy).toHaveBeenCalledTimes(0);
expect(showMessageResponse).toBe(`The sum is 123`);
});
});
) also posted in this sandbox
Only methods can be spied. There is no way to spy on funcB if it's called directly like funcB() within same module.
In order for exported function to be spied or mocked, funcA and funcB should reside in different modules.
This allows to spy on funcB in transpiled ES module (module object is read-only in native ESM):
import { funcB } from './b';
export const funcA = () => {
funcB()
}
Due to that module imports are representations of modules, this is transpiled to:
var _b = require('./b');
var funcA = exports.funcA = function funcA() {
(0, _b.funcB)();
};
Where funcB method is tied to _b module object, so it's possible to spy on it.
The problem you describe is referenced on a jest issue.
A possible solution to your problem (if you want to keep the functions inside the same file) is to use CommonJS, consider the following example:
fns.js
exports.funcA = () => {
exports.funcB();
};
exports.funcB = () => {};
fns.spec.js
const fns = require("./fns");
describe("funcA", () => {
it("calls funcB", () => {
fns.funcB = jest.fn();
fns.funcA();
expect(fns.funcB).toBeCalled();
});
});
I ended up ditching the default export:
// UniqueIdGenerator.js
export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
And then I could use and spy it like this:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
Some recommend wrapping them in a const object, and exporting that. I suppose you can also use a class for wrapping.
However, if you can't modify the class there's still a (not-so-nice) solution:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'default');
Here is a way of doing it for a default export without modifying the import (or even needing an import in the test at all):
const actual = jest.requireActual("./UniqueIdGenerator");
const spy = jest.spyOn(actual, "default");