How to test parent method called from child component? - reactjs

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.

Related

React testing using shallow- Not able to call. props() and its subsequent functions since the component is controlled by a state

am just learning testing in React using shallow
I have a parent Component and a child component
The parent component has one state defined as:
const [searchOpen, setSearchOpen] = useState(false);
const performAnAction = () => {
setSearchOpen(false);
}
const changeSearch = async () => {
await setSearchOpen(true);
};
and inside the return of this parent component is calling the child component, but it does check the condition flag searchOpen , like -
return (
if(searchOpen &&
<childComponent performAnAction= {performAnAction} changeSearch={changeSearch}/>
}
)
where performAnAction is a function.
But in the parentComponent.test.js
it('should render the Child Component', async () => {
const component = shallow(<ParentComponent {...props} />);
const child = component.find('childComponent');
expect(child).toBeDefined();
await child.props().performAnAction();
});
But am getting this error:
Method “props” is meant to be run on 1 node. 0 found instead.
When I remove the flag, it's working as expected. But since on the page load it's false, I am not sure if it gets in the DOM or not.
So my guess is somehow we need to make the flag true in test case file, correct me if I'm wrong.
Guys this is very minimal code for you to understand, feel free to comment, if any issues, since am new in the platform
In your return line, you are using the searchOpen variable, which is false by default on initialisation.
In your test, shallow(<ParentComponent {...props} />);, ParentComponent will have its searchOpen state initialised to false, that's why your return won't return the ChildComponent. The error is normal!
To be able to render the childComponent, you either need to
Remove the condition
Initialise searchOpen to true (with useState(true))
Or call the changeSearch function in your test, to set the searchOpen variable to true:
it('should render the Child Component', async () => {
const wrapper = shallow(<ParentComponent {...props} />);
// Call changeSearch function to update searchOpen's value
const parentComponent = wrapper.instance();
parentComponent.changeSearch();
wrapper.update(); // to update the component's state
const child = wrapper.find('childComponent');
expect(child).toBeDefined();
await child.props().performAnAction();
});

How can we get the updated props in the same function, in a react functional component?

I'm using redux to manage my state. After making an API call, I would update my redux store, the component would receive the updated props from redux and I would handle update the state based on the props.
With class components I currently have a method that does this:
onEdit = async () => {
if(!this.props.item) {
await this.props.fetchItem();
}
this.setState({
item: this.props.item
});
}
The updated props would be used in the setState.
Here is an example of something similar with a functional component:
const Component = (item) => {
...
const onEdit = async () => {
if(!item) {
await this.props.fetchItem();
}
setState(item) // this doesn't work
};
...
}
Obviously the above doesn't work since item uses the same props as before.
I recognize that useEffect is probably the solution most people would go for, but I was just wondering if there was a similar solution to the class component method above, since the syntax is very nice.
Put the item into state instead of props, and use props as the parameter to the Component instead of item:
const Component = (props) => {
const [item, setItem] = useState();
const onEdit = () => {
if(!item) {
props.fetchItem().then(setItem).catch(handleError);
}
};
// ...
};

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();
});
});

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

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

How to unit test a React component that renders after fetch has finished?

I'm a Jest/React beginner. In jest's it I need to wait until all promises have executed before actually checking.
My code is similar to this:
export class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { /* Some state */ };
}
componentDidMount() {
fetch(some_url)
.then(response => response.json())
.then(json => this.setState(some_state);
}
render() {
// Do some rendering based on the state
}
}
When the component is mounted, render() runs twice: once after the constructor runs, and once after fetch() (in componentDidMount()) finishes and the chained promises finish executing).
My testing code is similar to this:
describe('MyComponent', () => {
fetchMock.get('*', some_response);
it('renders something', () => {
let wrapper = mount(<MyComponent />);
expect(wrapper.find(...)).to.have.something();
};
}
Whatever I return from it, it runs after the first time render() executes but before the second time. If, for example, I return fetchMock.flush().then(() => expect(...)), the returned promise executes before the second call to render() (I believe I can understand why).
How can I wait until the second time render() is called before running expect()?
I'd separate concerns, mainly because is easier to maintain and to test. Instead of declaring the fetch inside the component I'd do it somewhere else, for example in a redux action (if using redux).
Then test individually the fetch and the component, after all this is unit testing.
For async tests you can use the done parameter on the test. For example:
describe('Some tests', () => {
fetchMock.get('*', some_response);
it('should fetch data', (done) => { // <---- Param
fetchSomething({ some: 'Params' })
.then(result => {
expect(result).toBe({ whatever: 'here' });
done(); // <--- When you are done
});
});
})
The you can tests your component by just sending the loaded data in the props.
describe('MyComponent', () => {
it('renders something', () => {
const mockResponse = { some: 'data' };
let wrapper = mount(<MyComponent data={mockResponse}/>);
expect(wrapper.find(...)).to.have.something();
});
});
When it comes to testing you need to keep it simple, if your component is difficult to test, then there's something wrong with your design ;)
I've had some success with this, as it doesn't require wrapping or modifying components. It is however assuming there's only one fetch() in the component, but it can be easily modified if needed.
// testhelper.js
class testhelper
{
static async waitUntil(fnWait) {
return new Promise((resolve, reject) => {
let count = 0;
function check() {
if (++count > 20) {
reject(new TypeError('Timeout waiting for fetch call to begin'));
return;
}
if (fnWait()) resolve();
setTimeout(check, 10);
}
check();
});
}
static async waitForFetch(fetchMock)
{
// Wait until at least one fetch() call has started.
await this.waitUntil(() => fetchMock.called());
// Wait until active fetch calls have completed.
await fetchMock.flush();
}
}
export default testhelper;
Then you can use it just before your assertions:
import testhelper from './testhelper.js';
it('example', async () => {
const wrapper = mount(<MyComponent/>);
// Wait until all fetch() calls have completed
await testhelper.waitForFetch(fetchMock);
expect(wrapper.html()).toMatchSnapshot();
});
I found a way to do what I originally asked. I have no opinion (yet) whether it is good strategy or not (in fact I had to refactor the component immediately afterwards, so this question is no longer relevant to what I'm doing). Anyway, here is the testing code (explanation below):
import React from 'react';
import { mount } from 'enzyme';
import { MyComponent } from 'wherever';
import fetchMock from 'fetch-mock';
let _resolveHoldingPromise = false;
class WrappedMyComponent extends MyComponent {
render() {
const result = super.render();
_resolveHoldingPromise && _resolveHoldingPromise();
_resolveHoldingPromise = false;
return result;
}
static waitUntilRender() {
// Create a promise that can be manually resolved
let _holdingPromise = new Promise(resolve =>
_resolveHoldingPromise = resolve);
// Return a promise that will resolve when the component renders
return Promise.all([_holdingPromise]);
}
}
describe('MyComponent', () => {
fetchMock.get('*', 'some_response');
const onError = () => { throw 'Internal test error'; };
it('renders MyComponent appropriately', done => {
let component = <WrappedMyComponent />;
let wrapper = mount(component);
WrappedMyComponent.waitUntilRender().then(
() => {
expect(wrapper.find('whatever')).toBe('whatever');
done();
},
onError);
});
});
The main idea is that, in the testing code, I subclass the component (if this was Python I'd probably monkey-patch it, which works more or less the same way in this case) so that its render() method sends a signal that it executed. The way to send the signal is by manually resolving a promise. When a promise is created, it creates two functions, resolve and reject, which when called terminate the promise. The way to have code outside the promise resolve the promise is by having the promise store a reference to its resolve function in an external variable.
Thanks to fetch-mock author Rhys Evans who kindly explained the manually-resolve-promise trick to me.

Resources