jest spyOn mockimplementation on callback as argument not working - reactjs

Here I'm trying to mock a function whose args is a callback function which setState inside. but I'm not able to cover this piece of code in code coverage.
Is this the right way to mock this function?
const func =()={
//some other logic
window.__promos.holiday.event('gift',(data)=>{
setState(data)
})
}
test('window.__promo calls the callback function with the correct data', () => {
// Spy on the callback function and provide a mock implementation
jest.spyOn(window.__promo.holiday, 'event').mockImplementation(('gift', cb) => {
cb('data');
});
});

Related

Trying to spyOn a mocked function

I am trying to perform a spyOn on a function that exists in a mocked class:
test.ts -
import Service from '../base_classes/Service';
jest.mock('../base_classes/Service');
const o: Component = new Component();
it('should load model specific information', async () => {
const getSpy = jest.spyOn(Service.prototype, 'get');
let t = await o.load(1);
expect(t.name).toEqual('test_name');
expect(getSpy).toHaveBeenCalledTimes(1);
});
__mocks__/Service.ts -
export const mockGet = jest.fn(async () => Promise.resolve({name:
'test_name'}));
const mock = jest.fn().mockImplementation(() => {
return {
get: mockGet
}
});
export default mock;
The resulting error is: Cannot spy the get property because it is not a function
I tried changing the mocked arrow function to a function(), but that didn't help.
How can I set this up so I can spy on my mocked function?
When using jest.mock it automatically creates a mock for each property of the module, replacing it's functions with jest.fn() (without implementation). And from then on you can write assertions. There should be no reason you'd want to spy on a mock function as it's already a mock function.
You should either spy on the actual implementation or write assertions for the methods on the mocked module.
e.g.
it('should load model specific information', async () => {
let t = await o.load(1);
expect(t.name).toEqual('test_name');
expect(Service.mock.instances[0].get).toHaveBeenCalledTimes(1);
});
working example

Testing Sentry with Jest

I am testing my error boundaries to React and noticed in Codecov that there is a particular part of my Sentry function that hasn't been tested.
I have tried to use jest.mock("#sentry/browser") and mocking Sentry, however, can't seem to get the lines tested. The Sentry import is mocking correctly but not scope.
Here is an example of my attempt at mocking.
import * as Sentry from "#sentry/browser"
const mock_scope = jest.fn(() => {
return { setExtras: null }
})
Sentry.withScope = jest.fn().mockImplementation(mock_scope)
The untested lines are this callback function being passed to Sentry.withScope:
scope => {
scope.setExtras(errorInfo);
Sentry.captureException(error);
}
Since Sentry.withScope has been mocked you can use mockFn.mock.calls to retrieve the callback function passed to it.
Once you have retrieved the callback function, you can call it directly to test it.
Here is a slightly simplified working example:
import * as Sentry from '#sentry/browser';
jest.mock('#sentry/browser'); // <= auto-mock #sentry/browser
const componentDidCatch = (error, errorInfo) => {
Sentry.withScope(scope => {
scope.setExtras(errorInfo);
Sentry.captureException(error);
});
};
test('componentDidCatch', () => {
componentDidCatch('the error', 'the error info');
const callback = Sentry.withScope.mock.calls[0][0]; // <= get the callback passed to Sentry.withScope
const scope = { setExtras: jest.fn() };
callback(scope); // <= call the callback
expect(scope.setExtras).toHaveBeenCalledWith('the error info'); // Success!
expect(Sentry.captureException).toHaveBeenCalledWith('the error'); // Success!
});
Note that this line:
const callback = Sentry.withScope.mock.calls[0][0];
...is getting the first argument of the first call to Sentry.withScope, which is the callback function.
An addition to the accepted answer. The solution there requires to manually invoke the callback (see the callback(scope); // <= call the callback line in the test code).
Here is how to make it work automatically:
import * as Sentry from '#sentry/browser'
jest.mock('#sentry/browser')
// Update the default mock implementation for `withScope` to invoke the callback
const SentryMockScope = { setExtras: jest.fn() }
Sentry.withScope.mockImplementation((callback) => {
callback(SentryMockScope)
})
And then the test code becomes:
test('componentDidCatch', () => {
componentDidCatch('the error', 'the error info');
expect(SentryMockScope.setExtras).toHaveBeenCalledWith('the error info');
expect(Sentry.captureException).toHaveBeenCalledWith('the error');
});

React and Jest: How to mock implementation of member function

I'm doing some api fetching in my component. When doing unit testing, I'd like to mock the implementation of certain member functions:
//component.js
class Foo extends Component {
prepareData() {
getSthFromApi().then(getMoreFromApi).then(val=>this.setState({val}));
}
componentDidMount() {
this.prepareData();
}
}
//test.js
//What should this be?
Foo.prepareData = jest.fn().mockImplementation(() => {
this.setState({val:1});
})
const comp = shallow(<Foo />);
How should I do it?
You shouldn't mock your member function, instead you can mock getSthFromApi function and test both componentDidmount and prepareData together.
import { getSthFromApi } from "some-module";
jest.mock("some-module");
// in your test
getSthFromApi.resolves(1);
// here you can expect val to be 1 in your state.
The problem with this code is that Foo.prepareData is static method, while prepareData is instance method.
Since prepareData is prototype method, it can be mocked on class prototype; this is one of the reasons why prototype methods are preferable:
jest
.spyOn(Foo.prototype, 'prepareData')
.mockImplementation(function () {
this.setState({val:1});
});
Notice that mockImplementation should be provided with regular function because it doesn't need lexical this.

React Jest - Mock a function with callback

I'm trying to write a test for following code
var throttle = require('lodash.throttle');
search = throttle(async (searchTerm:string) => {
const response = await AxiosWrapper.Instance.post(this.props.url, { "searchTerm": searchTerm });
this.setState({
searchResult: response.data as ISearchResult,
showSearchResult: true
});
},500);
So, my mock looks like
jest.mock("lodash.throttle", () => {
console.log("blah");
});
I would like to execute callback from throttle func.
If you look at the jest docs for jest.mock, the second argument is a factory (function) that returns the mock value for the module. In this case, we are mocking the lodash.throttle function, so we want a factory that returns a function. If you want to mock lodash.throttle such that it just calls the callback that is passed in, you would do
jest.mock("lodash.throttle", () => cb => cb())

Jest - Mock Nested Function

I am writing a JEST/Enzyme test case for module.
One of the function I am testing in my component is about saving an xml file. Which in turn calls a library function fileDownload from 'react-file-download'
const saveContentToXML = () => {
if(this.props.message && this.props.message.details){
fileDownload(this.props.message.details, this.props.message.title);
}
}
When I wrote test case, it calls saveContentToXML and in turn calls fileDownload. This results in exception.
TypeError: window.URL.createObjectURL is not a function
My test case looks like
test('Save Content as XML Test', () =>{
const component = shallow(<Message details={details} />);
component.instance().saveContentToXML();
});
How do I test this function?
You should mock react-file-download and then assert that it is being called
// Default mock will just make fileDownload a jest mock function
jest.mock('react-file-download')
import fileDownload from 'react-file-download'
test('Save Content as XML Test', () =>{
const component = shallow(<Message details={details} />);
component.instance().saveContentToXML();
expect(fileDownload).toHaveBeenCalledWith('details', 'title');
fileDownload.mockClear() // resets the mock function so the call is not used in assertions for other tests
});
The jest documentation on mock functions is a good resource to refer to: https://facebook.github.io/jest/docs/mock-functions.html

Resources