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')
Cannot spy on individual functions that are individually exported
ecmascript 6 - Spying on an imported function that calls another function in Jest - Stack Overflow
spy on pure functions exported as const
How to mock only a non-default function in Jest
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();
});
});
Lately I've been writing pure functions more and more in my code, they convey meaning very well, are easy to write and test
Now that most of my logic is in pure function I tend to write them outside of components because they don't depend on the state of the component.
Example getUsername is pure and not used in the template and as such doesn't really need to be a method of the component.
@Component({selector: ..., templateUrl: ..., styleUrls: ...})
export class UserComponent implements OnInit {
@Input() user: User;
username: string
ngOnInit() {
this.username = getUsername(this.user);
}
}
export const getUsername = (user: User): string => `${user.firstname} ${user.lastname}`;When I have too many of these pure functions I sometimes move them to a helper file.
Everything works perfectly, except when I try to spy on them for unit testing. At this point jasmine refuses to spy on them (relevant github issue)
What do you guys think I should do ? Move my pure functions to method of components ? Change my helper files into services eventhough they don't use any state ?
I feel like I have to remove relevant information from my code (purity of the functions) for the sole reason of unit testing and I don't like it
Edit: spying on an exported function used to work but stopped working with Angular 9, cf this github issue