Jest -- Mock a function called inside a React Component - unit-testing

Jest provides a way to mock functions as described in their docs
apiGetMethod = jest.fn().mockImplementation(
new Promise((resolve, reject) => {
const userID = parseInt(url.substr('/users/'.length), 10);
process.nextTick(
() => users[userID] ? resolve(users[userID]) : reject({
error: 'User with ' + userID + ' not found.',
});
);
});
);
However these mocks only seem to work when the function is called directly in a test.
describe('example test', () => {
it('uses the mocked function', () => {
apiGetMethod().then(...);
});
});
If I have a React Component defined as such how can I mock it?
import { apiGetMethod } from './api';
class Foo extends React.Component {
state = {
data: []
}
makeRequest = () => {
apiGetMethod().then(result => {
this.setState({data: result});
});
};
componentDidMount() {
this.makeRequest();
}
render() {
return (
<ul>
{ this.state.data.map((data) => <li>{data}</li>) }
</ul>
)
}
}
I have no idea how to make it so Foo component calls my mocked apiGetMethod() implementation so that I can test that it renders properly with data.
(this is a simplified, contrived example for the sake of understanding how to mock functions called inside react components)
edit: api.js file for clarity
// api.js
import 'whatwg-fetch';
export function apiGetMethod() {
return fetch(url, {...});
}

You have to mock the ./api module like this and import it so you can set the implemenation of the mock
import { apiGetMethod } from './api'
jest.mock('./api', () => ({ apiGetMethod: jest.fn() }))
in your test can set how the mock should work using mockImplementation:
apiGetMethod.mockImplementation(() => Promise.resolve('test1234'))

In case the jest.mock method from #Andreas's answer did not work for you. you could try the following in your test file.
const api = require('./api');
api.apiGetMethod = jest.fn(/* Add custom implementation here.*/);
This should execute your mocked version of the apiGetMethod inside you Foo component.

Here is an updated solution for anyone struggling with this in '21. This solution uses Typescript, so be aware of that. For regular JS just take out the type calls wherever you see them.
You import the function inside your test at the top
import functionToMock from '../api'
Then you indeed mock the call to the folder outside of the tests, to indicate that anything being called from this folder should and will be mocked
[imports are up here]
jest.mock('../api');
[tests are down here]
Next we mock the actual function we're importing. Personally I did this inside the test, but I assume it works just as well outside the test or inside a beforeEach
(functionToMock as jest.Mock).mockResolvedValue(data_that_is_returned);
Now here's the kicker and where everyone seems to get stuck. So far this is correct, but we are missing one important bit when mocking functions inside a component: act. You can read more on it here but essentially we want to wrap our render inside this act. React testing library has it's own version of act. It is also asynchronous, so you have to make sure your test is async and also define the destructured variables from render outside of it.
In the end your test file should look something like this:
import { render, act } from '#testing-library/react';
import UserGrid from '../components/Users/UserGrid';
import { data2 } from '../__fixtures__/data';
import functionToMock from '../api';
jest.mock('../api');
describe("Test Suite", () => {
it('Renders', async () => {
(functionToMock as jest.Mock).mockResolvedValue(data2);
let getAllByTestId: any;
let getByTestId: any;
await act(async () => {
({ getByTestId, getAllByTestId } = render(<UserGrid />));
});
const container = getByTestId('grid-container');
const userBoxes = getAllByTestId('user-box');
});
});

Another solution to mock this would be:
window['getData'] = jest.fn();

Related

How to jest.spyOn mock implementation only for the first call then use default implementation?

I would like to use jest.spyOn to mock the implementation of a method only for the first call. On 2nd 3rd ...nth call want to call it's actual previous implementation.
I tried the below in ts and it is not working:
import handler from '../src/handler';
import * as cRedis from '../src/redis';
jest.spyOn(cRedis, 'rGetAsync');
describe('handle cart quantity validation', () => {
test('test 1 blabla', async () => {
(cRedis.rGetAsync as jest.Mock).mockImplementationOnce(
() =>
new Promise(resolve =>
setTimeout(() => {
resolve('{}');
}, 1000),
),
);
const response = await handler();
});
});
the handler function calls the method rGetAsync of cRedis two times.
just for illustrative example:
handler.ts
import { rGetAsync } from './redis';
export default function () {
const a = await rGetAsync('a');
const b = await rGetAsync('b');
console.log(a, b);
}
My problem is that the mockedImplementation is used in both calls!
So mockImplementationOnce is not really mocking it once.
I expect that for first call to use the mock implementation and second one the real one.
How can I achieve this with jest?
I have used your code and the following test:
import * as cRedis from '../redis';
import handler from '../handler';
describe('handle cart quantity validation', () => {
it('test 1 blabla', async () => {
const spy = jest.spyOn(cRedis, 'rGetAsync');
spy.mockResolvedValueOnce('Not Original')
await handler();
});
});
My redis looks like this:
export const rGetAsync = async () => Promise.resolve('Original implementation');
And my output is:
console.log
Not Original Original implementation
So it works properly.
You can check my post about clear/reset/restore of spies and mocks.

Mocking default exported function with Jest says it wasn't called but it was

[EDIT - POSSIBLE SOLUTION]
So I realised that my componentWillMount is an async method since it is using an async fs wrapper to do fs operations. So I made the beforeEach function argument async and awaited on the Enzyme.shallow. This seems to have worked. It just came to me that if it's async maybe the lifecycle hadn't run yet when the expectation was ran... What do you think?
It now looks like this
// root/Meetings/__tests__/MeetingsScreen.test.js
...
import sortMeetings from '../../helpers/sort';
jest.mock('../../helpers/sort', () => jest.fn());
describe('MeetingsScreen', () => {
let wrapper;
const mockValueForMeetings = [];
sortMeetings.mockReturnValue(mockValueForMeetings);
beforeEach(async () => {
wrapper = await Enzyme.shallow(<MeetingsScreen />);
});
it('should call the sort method', () => {
expect(sortMeetings).toHaveBeenCalled();
});
});
[ORIGINAL QUESTION]
I am mocking an imported function and the test says that it wasn't called but it returns the stubbed value.
I have this class/screen in react-native that imports a helper file that only has one function to do a sort.
// root/helpers/sort.js
import moment from 'moment';
const compareDateTime = (a, b) => {
...
};
const sortMeetings = meetings => meetings.sort(compareDateTime);
export default sortMeetings;
My class looks like this
// root/Meetings/MeetingsScreen.js
...
import sortMeetings from '../helpers/sort';
export default class MeetingsScreen extends Component {
...
componentDidMount() {
this.updateState();
}
updateState = async () => {
const meetingsOnFile = await fsStorage.getItem('meetings'); // this is also stubbed and returns an [{}]
const meetings = sortMeetings(meetingsOnFile);
this.setState({ meetings });
}
render() {
return (
<MeetingList meetings={this.state.meetings} />
);
}
}
And this it my test. I am using Jest.
// root/Meetings/__tests__/MeetingsScreen.test.js
...
import sortMeetings from '../../helpers/sort';
jest.mock('../../helpers/sort', () => jest.fn());
describe('MeetingsScreen', () => {
let wrapper;
const mockValueForMeetings = [];
sortMeetings.mockReturnValue(mockValueForMeetings);
beforeEach(() => {
wrapper = Enzyme.shallow(<MeetingsScreen />);
});
it('should call the sort method', () => {
expect(sortMeetings).toHaveBeenCalled();
});
});
So If I got it right, since the import of the default function returns a function when using Jest i am mocking with a function. Before the test I am setting that mock to always return an empty array and I am printing out down the code after the call of the function (in the source code) and it indeed returns an array. I changed it to others values as well (i.e. 13, [2, 3, 4], et.c) and they all get returned. So I would assume that the mock function gets called. But the expectation fails. If I print the sortMeetings.mock as well, it just shows empty arrays in it's values {"calls":[],"instances":[],"timestamps":[]}
Could someone point to the mistake I am doing. I think I might be wrong about how import default functions work or how Jest is doing the mocking

How to test react component correctly?

Recently I am learning to test React with jest and enzyme, It seems hard to understand what a unit test is it, my code
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const value = e.target.value;
this.setState({
value
});
}
render() {
return <Nest value={this.state.value} handleChange={this.handleChange} />;
}
}
export const Nest = props => {
return <input value={props.value} onChange={props.handleChange} />;
};
export default App;
and my test
import React from "react";
import App, { Nest } from "./nest";
import { shallow, mount } from "enzyme";
it("should be goood", () => {
const handleChange = jest.fn();
const wrapper = mount(<App />);
wrapper.find("input").simulate("change", { target: { value: "test" } });
expect(handleChange).toHaveBeenCalledTimes(1);
});
IMO, the mocked handleClick will intercept the handleClick on App,
if this is totally wrong, what's the right way to use mock fn and test the handleClick be called.
Another: I search a lot, read the similar situations, seem like this iscontra-Unit Test,
Probably I should test the two component separately, I can test both components,
test the
<Nest value={value} handleChange={handleChange} />
by pass the props manually, and then handleChangeinvoked by simulate change
it passed test.
but how can I test the connection between the two?
I read
some work is React Team's Work
...
I don't know which parts I have to test in this case, and Which parts react already tested and don't need me to test. That's confusing.
You should take the path of testing the Nest component in isolation first, passing your mocked handleChange as a prop, to verify that input changes are being propagated.
If you want to test the state part, then you can get the instance of your App class from enzyme and call that method directly:
it("should update the Nest value prop when change is received", () => {
const wrapper = mount(<App />);
const instance = wrapper.instance()
instance.handleChange( { target: { value: "test" } })
const nestComponent = wrapper.find("Nest").first()
expect(nestComponent).prop('value').toEqual('test');
});
This a very very basic, almost not needed to test piece of code, but it will get your test coverage up if that's what you're after.
Doc for instance: http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html
If you want to test for the connection. From what I see, the nest component is a child component inside the App component. You could test that <App /> contains `.
describe('<App />', () => {
it('should contain a nest component', () => {
const wrapper = mount(<App />);
expect(wrapper.find(<Nest />)).toHaveLength(1);
});
});
Secondly, since the onChange event on the nest component updates the state in the App component, you can also test for state changes since its a behavior you expect.
it('should update state', () => {
//find input and simulate change with say {value: 'new value'} and then
expect(wrapper.state().value).toBe('newValue');
});
I hope this helps.

What is the point of unit testing redux-saga watchers?

In order to get 100% coverage of my Saga files I'm looking into how to test watchers.
I've been googling around, there are several answers as to HOW to test watchers. That is, saga's that do a takeEvery or takeLatest.
However, all methods of testing seem to basically copy the implementation. So what's the point of writing a test if it's the same?
Example:
// saga.js
import { delay } from 'redux-saga'
import { takeEvery, call, put } from 'redux-saga/effects'
import { FETCH_RESULTS, FETCH_COMPLETE } from './actions'
import mockResults from './tests/results.mock'
export function* fetchResults () {
yield call(delay, 1000)
yield put({ type: FETCH_COMPLETE, mockResults })
}
export function* watchFetchResults () {
yield takeEvery(FETCH_RESULTS, fetchResults)
}
Test method 1:
import { takeEvery } from 'redux-saga/effects'
import { watchFetchResults, fetchResults } from '../sagas'
import { FETCH_RESULTS } from '../actions'
describe('watchFetchResults()', () => {
const gen = watchFetchResults()
// exactly the same as implementation
const expected = takeEvery(FETCH_RESULTS, fetchResults)
const actual = gen.next().value
it('Should fire on FETCH_RESULTS', () => {
expect(actual).toEqual(expected)
})
})
Test method 2: with a helper, like Redux Saga Test Plan
It's a different way of writing, but again we do basically the same as the implementation.
import testSaga from 'redux-saga-test-plan'
import { watchFetchResults, fetchResults } from '../sagas'
import { FETCH_RESULTS } from '../actions'
it('fire on FETCH_RESULTS', () => {
testSaga(watchFetchResults)
.next()
.takeEvery(FETCH_RESULTS, fetchResults)
.finish()
.isDone()
})
Instead I'd like to simply know if watchFestchResults takes every FETCH_RESULTS. Or even only if it fires takeEvery(). No matter how it follows up.
Or is this really the way to do it?
It sounds like the point of testing them is to achieve 100% test coverage.
There are some things that you can unit test, but it is questionable if you should.
It seems to me that this situation might be a better candidate for an 'integration' test. Something that does not test simply a single method, but how several methods work together as a whole. Perhaps you could call an action that fires a reducer that uses your saga, then check the store for the resulting change? This would be far more meaningful than testing the saga alone.
I agree with John Meyer's answer that this is better suited for the integration test than for the unit test. This issue is the most popular in GitHub based on up votes. I would recommend reading it.
One of the suggestions is to use redux-saga-tester package created by opener of the issue. It helps to create initial state, start saga helpers (takeEvery, takeLatest), dispatch actions that saga is listening on, observe the state, retrieve a history of actions and listen for specific actions to occur.
I am using it with axios-mock-adapter, but there are several examples in the codebase using nock.
Saga
import { takeLatest, call, put } from 'redux-saga/effects';
import { actions, types } from 'modules/review/reducer';
import * as api from 'api';
export function* requestReviews({ locale }) {
const uri = `/reviews?filter[where][locale]=${locale}`;
const response = yield call(api.get, uri);
yield put(actions.receiveReviews(locale, response.data[0].services));
}
// Saga Helper
export default function* watchRequestReviews() {
yield takeLatest(types.REVIEWS_REQUEST, requestReviews);
}
Test example using Jest
import { takeLatest } from 'redux-saga/effects';
import { types } from 'modules/review/reducer';
import SagaTester from 'redux-saga-tester';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import watchRequestReviews, { requestReviews } from '../reviews';
const mockAxios = new MockAdapter(axios);
describe('(Saga) Reviews', () => {
afterEach(() => {
mockAxios.reset();
});
it('should received reviews', async () => {
const services = [
{
title: 'Title',
description: 'Description',
},
];
const responseData = [{
id: '595bdb2204b1aa3a7b737165',
services,
}];
mockAxios.onGet('/api/reviews?filter[where][locale]=en').reply(200, responseData);
// Start up the saga tester
const sagaTester = new SagaTester({ initialState: { reviews: [] } });
sagaTester.start(watchRequestReviews);
// Dispatch the event to start the saga
sagaTester.dispatch({ type: types.REVIEWS_REQUEST, locale: 'en' });
// Hook into the success action
await sagaTester.waitFor(types.REVIEWS_RECEIVE);
expect(sagaTester.getLatestCalledAction()).toEqual({
type: types.REVIEWS_RECEIVE,
payload: { en: services },
});
});
});

How to unit test an angular 2 component when I don't care about the dependencies? I just want to test some inner functions

I'm new to writing unit tests, and unfortunately I've already built a few "complicated" (for me) components which I am having a hard time even beginning to write tests for.
Here's a snippet of my code, including the constructor. Basically, I don't really care right now about these dependencies, I want to test some inner functions such as resizing based on array size, etc. For these, I can just create an Array.fill and should be good to go.
export class GalleryComponent implements OnInit {
photos = [];
galleryState: Observable<any>;
constructor(
private store: Store<any>,
private photoActions: PhotoActions,
private router: Router
) {
this.galleryState = this.store.select<any>(state => state.photos.gallery);
}
}
In my other components which have nothing in the constructor, instantiating the component in my test is as simple as new SomeComponent().
However, in the GalleryComponent above, I am wondering if there is a way that I can literally ignore the dependencies completely (for now), and instead just instantiate the component in a way that I can test some inner functions easily. For example, say I had the following function inside GalleryComponent:
function timesByTwo(number) {
return number * 2;
}
This is not at all related to any of the dependencies, so how can I just test that one function given that this component has 3 dependencies?
Thanks
If you truly don't care about testing anything at all that is associated with your dependencies, then in your spec you can just construct your component with null values for those dependencies.
import { AppComponent } from './app.component';
describe('App: Test', () => {
let component: AppComponent;
beforeEach(() => {
component = new AppComponent(null, null, null);
});
it('should create the app', () => {
expect(component).toBeTruthy();
});
it(`Should return 4`, () => {
expect(component.timesByTwo(2)).toEqual(4);
});
}
To get around your current usage of this.store.select in your constructor you can modify your constructor like so
constructor(
private store: Store<any>,
private photoActions: PhotoActions,
private router: Router
) {
if(this.store == null){
this.galleryState = null;
}else{
this.galleryState = this.store.select<any>(state => state.photos.gallery);
}
}
Otherwise you can mock your Store component in your test page. An example
import { Observable } from 'rxjs/Rx'
export class MockService extends EventService{
constructor() { super(null); }
getEvents(user:string){
return Observable.of([{val: "test"}]);
}
}
and then modify my code from above to be
let component: AppComponent;
let mockService: MockService;
beforeEach(() => {
mockService = new MockService()
component = new AppComponent(null, mockService, null);
});