How to unit test functions inside componentwillmount using jest and enzyme? - reactjs

I'm trying to test a function inside componentWillMount.
component
componentWillMount = () => {
const {
agents,
match
} = this.props;
this.edit = false;
this.agent = {};
if (match.params.id) {
this.edit = true;
this.agent = getAgent(agents, match.params.id);
if ("undefined" === typeof this.agent) {
push("/agents");
}
}
resetStatusMessage();
formResetError();
};
render = () => {
const { form } = this.props;
const agent = this.agent;
this.avatar = agent.avatar;
...........................
}
I'am trying to test whether the getAgent function is called.And i also need to check the resetStatusMessage() and formResetError() were called.
Tests:
it("should call getAgent when mounted", () => {
const match = {
params: {
id: "1"
}
},
agents ={
loading: false,
byId : {
1:{
firstName: "abc",
lastName: "xyz"
}
},
avatar: "avatarUrl"
};
let mockGetAgent = jest.fn();
const store = configureStore();
const wrapper = mount(
<Provider store={store}>
<AgentForm match={match} getAgent={mockGetAgent}/>
</Provider>
);
expect(wrapper).toBeDefined();
expect(mockGetAgent).toBeCalled();
});
But my test failed with this message :
TypeError: Cannot read property 'avatar' of undefined
How can i solve this issue?In my react project am using jest and enzyme for testing.am new to react and enzyme.Any help will really appreciable.

Apologies, I didn't mean you need to pass it in as a prop. This will only work if the component normally receives the getAgent function as a prop.
I'm guessing that getAgent is a function defined within the same file as your component but outside of the component itself, and that you're only exporting the component?
If this is the case, when you mount the component it will look for getAgent within its scope and try to call it. At the moment, you've created a function called mockGetAgent but the component never makes a call to mockGetAgent. I think what you need to do is call your mock getAgent and get it to return something (e.g. An object that looks like one of your agents) so that this.agent isn't undefined
Also, a couple of notes on unit testing:
you should try to test your components in isolation. Here you're testing both Provider and AgentForm at the same time, but given that they each do specific things you should just try to test they're each doing their own job.
it's not very effective to test a component by checking that every function it uses gets called. You should try to check that the job the function does has been completed. E.g. if the getAgent function gets info about agents so that it can be rendered then you should check that your wrapper contains that info, rather than checking that getAgent was called

Related

How to mock a third party React component using Jest?

TLDR; what's the proper way to mock a React component imported from a third-party library?
I'm testing a component called <App/>. It consumes a 3rd part component called <Localize/> provided by a library called localize-toolkit.
I'm having some trouble mocking <Localize/> using Jest.
Here is how I've tried mocking it.
jest.mock('localize-toolkit', () => ({
// Normally you pass in a key that represents the translated caption.
// For the sake of testing, I just want to return the key.
Localize: () => (key:string) => (<span>{key}</span>)
}));
And I've written a unit test for <App/> that looks like this.
it('Test', () => {
const component = render(<App/>);
expect(component).toMatchSnapshot();
}
)
It will pass, however this is the warning message returned.
Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render.
And when I look at the snapshot, I get a series of periods "..." where the localized caption should appear.
Am I not mocking the Localize component properly?
Here's how I ended up doing it.
Note how the third-party component Localize needs to be returned as a function.
jest.mock('localize-toolkit', () => ({
Localize: ({t}) => (<>{t}</>)
}));
and in case there are multiple components, and you only want to mock one of them, you can do this:
jest.mock("localize-toolkit", () => {
const lib = jest.requireActual("localize-toolkit");
return {
...lib,
Localize: ({t}) => (<>{t}</>),
};
});
We can mock the 3rd party library for example in my case i need to mock react-lazyload
Component.tsx
import LazyLoad from 'react-lazyload';
render() {
<LazyLoad><img/></LazyLoad>
}
In jest.config.js
module.exports = {
moduleNameMapper: {
'react-lazyload': '/jest/__mocks__/react-lazyload.js',
}
}
In jest/mocks/react-lazyload.js
import * as React from 'react';
jest.genMockFromModule('react-lazyload');
const LazyLoad = ({children}) => <>{children}</>;
module.exports = { default: LazyLoad };

How to test parent method called from child component?

I'm using enzyme and jest and I want to test that parent method is called when child props is called.
I have something like this
class Parent extends Component {
method = () => {...}
render() {
<Child propMethod={method}/>
}
}
And in test I do something like this
let shallow;
function setup() {
const props = {
mockMethod: jest.fn()
};
const enzymeWrapper = shallow(<Parent {...props}/>);
return {
props,
enzymeWrapper
};
}
beforeAll(() => {
shallow = createShallow({ dive: true });
});
describe('components', () => {
describe('Child', () => {
it('should call method', () => {
const { enzymeWrapper, props } = setup()
const component = enzymeWrapper.find(Child)
component.prop('propMethod')();
expect(props.mockMethod).toHaveBeenCalledTimes(1);
})
});
});
But I get
Expected number of calls: 1
Received number of calls: 0
Test structure is from Redux docs
here
Your mock method isn't making it to the child prop. From the comments what you probably want to do is test that when the method is called, it has some meaningful side effect. If it's ultimately dispatching a redux action, test that the action was dispatched when the child method is called.
You will have to do something like this anyways if you intend to give your present test any real constraint on the component behavior, so testing that directly is more straightforward and meaningful.

Jest testing that a React class method was called by componentWillMount

I am trying to write a test to assert that my class method is being called when the componentWillMount method fires when the component renders.
I have tried the Jest documentation in addition to researching this online. From the answers I've found (including on here) there seemed to be 2 possible methods of doing this.
The first was to:
shallow render the component
create a jest.fn of the class method I want to test,
call componentWillMount using wrapper.instance().componentWIllMount
assert that the method was called once
The second was to spy on the method I'm expecting to be called:
shallow render the component
set up the spy and assign to a constant e.g. functionSpy
call componentWillMount
assert the functionSpy was called how ever many times
The refresh method definitely fires whenever the component is rendered so I just need to work out how I can reflect this in a test.
The code base I am working on is for a civil service system so have to be really careful what I disclose, hopefully this will be enough for explaining the problem I'm having..
The class is structured:
export class Search extends AnErrorComponent {
static propTypes = {
.....
};
state = {
.....
}
componentWillMount(){
this.refresh();
}
refresh = () => {
.....
} // This is the method I'm trying to test
but can't seem to access/test.
search = () => {
.....
}
//etc
render(){
return(
...
);
}
}
To test this I've tried:
describe('Search component', () => {
it("should call the refresh method when the page loads", () => {
const store = makeStore();
const wrapper = shallow(<Search store={store}/>);
wrapper.instance().refresh = jest.fn();
wrapper.update();
wrapper.instance().componentWillMount;
expect(wrapper.instance().refresh).toHaveBeenCalledTimes(1);
});
});
The result of running this test is:
● Search component › should call the refresh method when the page loads
expect(jest.fn()).toHaveBeenCalledTimes(1)
Expected mock function to have been called one time, but it was called zero times.
I also tried:
describe('Search component', () => {
it("should call the refresh method when the page loads", () => {
const store = makeStore();
const wrapper = shallow(<Search store={store}/>);
const refreshSpy = spyOn(Search.prototype, 'refresh');
wrapper.instance().componentWillMount;
expect(refreshSpy).toHaveBeenCalledTimes(1);
});
});
I get the error:
● Search component › should call the refresh method when the page loads
refresh() method does not exist
This refers to the spy I tried to create.
I've double checked and I have imported the Search component in addition to the component it inherits from. I have also tried using mount instead of shallow rendering; however to make this work I had to wrap the component in a provider otherwise an error would be thrown e.g.
<provider store={store}>
<Search />
</provider>
I still got the same results after when using mount and wrapping the component in a provider. Due to the spy error I tried console logging wrapper.instance() in both tests and noted that none of the class methods are listed anywhere if this helps? Any help on this would be greatly appreciated. (This is the first question I've posted on here so hopefully this makes sense).
** Just to add, when using jest.spyOn() I get TypeError: jest.spyOn is not a function. I am using Jest 21.2.1 which I read should allow me to use jest.spyOn() as it was added in V19. **
componentWillMount is a method on the class instance, not a property. You need to call it to trigger the effect:
describe('Search component', () => {
it("should call the refresh method when the page loads", () => {
const store = makeStore();
const wrapper = shallow(<Search store={store}/>);
wrapper.instance().refresh = jest.fn();
wrapper.update();
wrapper.instance().componentWillMount(); // Calling the method
expect(wrapper.instance().refresh).toHaveBeenCalledTimes(1);
});
});
You need to call componentWillMount and spyOn the refresh function by Mock Implementation
describe('Search component', () => {
const store = makeStore();
const wrapper = shallow(<Search store={store}/>);
let refresh;
beforeAll(() => {
refresh = jest.spyOn(Search.prototype, 'refresh').mockImplementation(() => true);
});
it("should call the refresh method when the page loads", () => {
wrapper.instance().componentWillMount();
expect(refresh.mock.calls.length).toBe(1);
});
afterAll(() => {
refresh.mockRestore();
});
});

Reactjs test sinon spy mocked function returns a function and not a boolean

I'm trying to check that that formIsValid method in my InformationGatheringFormContainer component is calling one of the components props (isInfoFormValid) when executing:
export class InformationGatheringFormContainer extends React.Component{
...
formIsValid() {
this.props.isInfoFormValid(this.state.invalid);
}
To do that, I'm using the sinon spy function:
it('formIsValid changes the state', () => {
const mockFunction = sinon.spy();
const baseProps = {
isInfoFormValid: mockFunction,
}
const wrapper = shallow(<InformationGatheringFormContainer {...baseProps} />);
wrapper.instance().formIsValid();
expect(mockFunction).to.have.been.calledOnce.equal(true);
})
I would expect it to work, however this test gives:
AssertionError: expect(received).to.equal(expected)
Expected value to equal:
true
Received:
[Function proxy]
Difference:
Comparing two different types of values. Expected boolean but received function.
So the function call is indeed detected but the .to.have.been.calledOnce enzyme method does not return a boolean here apparently.
I'm new to Reactjs Unit tests, and I'm a bit lost. How can the return of the .to.have.been.calledOnce have a different type than boolean ?
Thank you in advance for your help
I also found another way to do it:
expect(mockFunction.callCount).toEqual(1);
Looks like calledOnce is a sinon spy property, not jest's expect.
So, something like:
expect(mockFunction.calledOnce).toEqual(true);
should work (if you prefer sinon).
Worth to note that jest has his own mocking mechanism:
it('formIsValid changes the state', () => {
const isInfoFormValid = jest.fn();
const baseProps = {
isInfoFormValid,
}
const wrapper = shallow(<InformationGatheringFormContainer {...baseProps} />);
wrapper.instance().formIsValid();
expect(isInfoFormValid).toHaveBeenCalledTimes(1);
})

Jest mock a function within object

Trying the following :
// Function Spies
const onSubmitSpy = jest.fn().mockName('onSubmitSpy');
const onHistoryPushSpy = jest.fn().mockName('onPushSpy');
// Default Props
const defaultProps = {
signupUserMutation: onSubmitSpy,
history: {
push: onHistoryPushSpy
}
};
Then within my test this spy gets called withing my code like this
history.push('/');
(I verified with mock Response) but the call count in test response always remains 0.
I have a feeling this is maybe because this is a nested object I am missing something ?
test('should submit and call routing change', () => {
...
expect(onHistoryPushSpy).toHaveBeenCalledTimes(1);
});
And I always get this error
Expected mock function to have been called one time, but it was called zero times.

Resources