How to test child component method with Enzyme? - javascript

I have a component like that:
<Parent>
<Child/>
</Parent>
and <Child/> component have a method foo. I want test the foo method but I don't know how to access it. I tried:
mount(<Parent><Child/></Parent>).props().children.foo
or
mount(<Parent><Child/></Parent>).children().foo
but both them are undefined. I can't use .instance() because it's not root. I can't mount <Child/> only because the <Parent> add something (react-router's context.router) on context and I need them when init <Child/>. Any idea with this?

This worked for me:
mount(<Parent><Child/></Parent>).find(Child).instance().foo

I would consider writing tests for only your parent class, and then a separate test file to only test your child.
Once you have mounted you component using:
const component = mount(<Child>);
you then have access to it's methods using:
component.instance().methodname
You can then do stuff like override it with jest.fn() and test appropriately.

I prefer shallow mount over full mount from enzyme.
In conjunction with proxyquire to resolve child component (which you want to test)
I do
wrapper.find('Child1').props().propName
And test it.
Or I use shallow
mount wrapper.dive()

I think your problem is way different from how to test child components.
My first question is: Why are you checking if a child component has a specific method in the parent's component tests?
IMHO you need to have a test specific for this component and, then, in this test you check if the method exists.
Just to not leave without the answer, did you tried .find(Child).instance().foo ?

I had a similar problem when trying to mock a function on an inner component within a MemoryRouter:
cont wrapper = mount(<MemoryRouter><AvailabilityButtonWithRouter.WrappedComponent vetId={ vetId } appointment={ availability } /></MemoryRouter>);
I ended up being able to mock the function like so:
const mockFn = jest.fn();
wrapper.children(0).children(0).instance().reloadCurrentPage = mockFn;

I was able to get a handle on child function like the following, i was looking for the first child to call the function on -
const component = shallow(<Component />);
component.find(Child).first().getNode().props.someChildFunction()

I faced a similar problem and I went through mount API by logging. In my use case, my child component(CommonStoresReactions) is wrapped with mobx inject.
const jsx = (
<Provider {...stores}>
<CommonStoresReactions>
<div />
</CommonStoresReactions>
</Provider>
)
const wrapper = mount(jsx)
I want to test clearStores method in CommonStoresReactions. Below snippet worked for me.
wrapper
.find(CommonStoresReactions)
.instance()
.wrappedInstance.clearStores()

Enzyme has an option for the mount API called wrappingComponent (and wrappingComponentProps) to wrap the mounted object inside another component for providing context, etc.
See https://github.com/airbnb/enzyme/blob/master/docs/api/mount.md#mountnode-options--reactwrapper

I managed to solve this by using dive
wrapper.dive().props().propName

With enzyme:
mount(<Parent><Child/></Parent>).childAt(0).instance().foo
There are valid reasons to access the child and call a method. If the parent is superficial and children have a consistent interface you can call methods without knowing which child it is, testing that all children have the correct interface, signature etc.

The best way I find out is using shallow wrapper's dive method. Here is the doc: enzyme dive doc
Remember if ur parent component use the fully rendering like mount, then the react wrapper itself doesnt have the dive method so u have to use shallow render.
Here is one example:
let instance, child, diveChild;
describe('Test Parent child Child component', () => {
beforeEach(() => {
wrapper = shallow(<Parent {...props} />);
child = wrapper.find('Child');
diveChild = child.dive();
console.log(diveChild.instance());
});
test('Child get mounted', () => {
expect(child.exists()).toBeTruthy();
expect(child.debug()).toMatchSnapshot();
});
});

I would start by echoing #rnmalone's answer that you probably don't want to test a function on a child directly. That's not really unit testing, that's integration testing. You don't want to test your foo method.
That said, you may want to grab children to test their bar value and see if they received something you did to them by manipulating the parent. And since, if you mount at least (?), the children are fully instantiated, there's no reason to spy on a shim; you can go straight to the "real" child and test it.
Here's a test that does both. We do test a foo on a child, so to speak -- it's an event handler -- and then we test its bar value -- in this case that the proper child had a value set to match what we raised in the foo event.
Testing if foo sets bar
In this test, we've spun up a component we're testing (we were using Preact instead of React and htm in place of JSX; apologies if I don't clean this perfectly):
var wrapper = Enzyme.mount(
<MyParentComponent
myItemTypes={arrayTypes}
mySelectedItem={someSelectedItem}
onTabClicked={mySpy.onTabClicked}
/>
);
Now MyParentComponent has child components in its markup called MyChildComponent.
(This is its "live" code, returned by a functional component, and is not from a test file, to be overly clear.)
return <ul>
{Object.keys(props.myItemTypes).map(function (key) {
var isSelected = myItemTypes[key] === mySelectedItem;
return
<MyChildComponent
isSelected={isSelected}
tabType={myItemTypes[key]}
onTabClicked={props.onTabClicked}
></MyChildComponent>
;
})}
</ul>;
So the parent component is, with respect to the onTablClicked event handler, basically just a passthrough.
Now I can spoof a click on a child item like this using ReactTestUtils:
var specificItem = wrapper
.find('MyChildComponent')
.findWhere((x) => x.props().tabType.value === newlySelectedTab.value);
if (specificItem) {
var onTabClicked = lessonsTab.props().onTabClicked;
TestUtils.act(function () {
onTabClicked(newlySelectedTab);
});
}
wrapper.update();
The nasty part here is that I registered that onTabClicked from this on the parent component:
onTabClicked={mySpy.onTabClicked}
That is, that act on the selected child will just call my spy, and my spy does this:
spyOn(mySpy, 'onTabClicked').and.callFake(function (tab) {
wrapper.setProps({ mySelectedItem: tab });
});
That's problematic. We'll discuss that later.
Now I can run a test to see if the child's prop was updated.
it('should send the new selection value to the child component', function () {
var allItems = wrapper.find(MyChildComponent);
var selectedItem = navItems.findWhere((x) => x.props().isSelected);
expect(selectedItem.props().settingsTab.value).toBe(newlySelectedTab.value);
});
But that really reduces to mock foo and real bar
Again, the weird part of doing that is the fake click on the child is really close to testing your mocks, which you shouldn't really do. (Reference is a powerpoint. Click at your own risk.)
I could've just tested initial state setting like this:
it('should send the new selection value to the child component', function () {
// Act
wrapper.setProps({ mySelectedItem: itemToSelect });
wrapper.update();
// Assert
var allItems = wrapper.find(MyChildComponent);
var selectedItem = navItems.findWhere((x) => x.props().isSelected);
expect(selectedItem.props().tabType.value).toBe(itemToSelect.value);
});
... and that's probably good enough for whatever work you're doing. It reduces to nearly the same thing.
I guess the bonus is that you know the event handler is registered on the child, and that's something. Though we should probably just test that foo fired using mySpy on MyParentComponent, right? And then add a second test to see that the child value changes when the parent prop does.
So likely still a code smell somewhere.

Related

React Testing Library: OnClick isn't working in a child component

I have a test like this:
it('Side effect should trigger', async ()=>{
await act(async () => {
let container = render(<Grandparent/>);
const btn = container.findByTestId('childBtn');
fireEvent.click(btn)
});
const changedMsg = await container.findByText('Changed');
expect(changedMsg).toBeInTheDocument();
})
My components look like this:
Grandparent:
const Grandparent = ()=>{
const [labelText, setLabelText] = useState('Unchanged');
return (<Parent labelText={labelText} setLabelText={setLabelText}/>);
}
Parent:
const Parent = ({labelText, setLabelText})=>{
return (<div>
<label>{labelText}</label>
<Child setLabelText={setLabelText} />
</div>)
}
Child:
const Child = ({setLabelText}) =>{
return(<div>
<Button data-testid='childBtn' onClick={()=>setLabelText('Changed')}/>
</div>)
}
My issue is that the assertion in my unit test fails. I've logged the return value from findByTextId('childBtn') and I am indeed getting a button (something like <button class='MuiButtonBase-root' data-testid='childBtn'></button>), but the side effect from clicking it is not going through. I added some logging in the onClick function and it never showed up when I re-ran the test, so the issue is that the onClick handler isn't firing at all.
How do I fix this? Is trying to test something that involves side effects from a bunch of nested components like this not feasible in the first place?
EDIT: I think you have some code mistakes in your unit test since you are mixing react-testing-library API and testing-library, try this:
it("Side effect should trigger", async () => {
const {findByTestId, findByText} = render(<Grandparent />);
fireEvent.click(await findByTestId("childBtn"));
const changedMsg = await findByText("Changed");
expect(changedMsg).toBeInTheDocument();
});
});
I did not test it, but it should work.
act() wrapper is not needed in react-testing-library since the render method is using it internally.
With react-testing-library you dont' have to care about implementation details of your components, nor you care to test internal state, etc... You just test "What the end user sees on screen in response to certain events". If you need to test functions, like fetch, interactions with global object, etc... you need to mock all those methods.
The philosophy of this approach is to make your unit tests ( which are never funny to write ) so that they won't have to change too often even if you are going to refactor your React Components. You just care about what it' s being rendered as DOM nodes and that's what you have to test.
NOTE: In the code you pasted there are typos btw, props have to be destructured in your code if you want to use them directly without doing props.prop, like : const Child = ({setLabelText}) =>{ .

Access state of a component in a test

I have a component that change some of his props when we inject different props.
I'm struggling to find a simple way to acces the state of my shallowed component from my test
Here is the code :
describe('componentWillReceiveProps', () => {
it('update state isDedicatedDealPriceSelected to true', () => {
const productComponent = shallow(<Product selectedPriceOption="Test" />);
productComponent.setProps({ selectedPriceOption: 'dedicatedDealPrice' });
expect(productComponent.props.isDedicatedDealPriceSelected).toBeTruthy();
});
});
I got undefined, i want to access the props isDedicatedDealPriceSelected that should be truthy. I think i'm miswritting something here on the last line in productComponent.props.isDedicatedDealPriceSelected
How can i access the props of my component ?
I'm using enzime to shallow render my component in the test with jest.
Thankd in advance !
EDIT : i was not looking to access the props, but instead the state ! sorry for the mispelling
It seems setProps takes a callback executed after the re-render. Maybe your test needs to be async and the assertion to be done inside this callback.
From the examples, only componentWillReceiveProps seems to be called synchronously.
https://airbnb.io/enzyme/docs/api/ShallowWrapper/setProps.html
To access the state of a shallow rendered component, you can access it using :
const wrapper = shallow(<component />);
wrapper.state().componentVar
To access a function of a shallow rendered component, you can access it using :
wrapper.instance().componentFunction()

Testing an isolated function of a component in React Jest

I am fairly new to Jest and Enzyme and I stumbled a cross a problem:
I have a Component which renders Children and also calls a method of those children. I achieve that by using refs. I call these functions something like:
somefunction = () => {
this.myReference.current.childFunction();
this.doSomethingOther();
}
I now want to test the function somefunction. I want to check if the doSomethingOther function was called. Using a shallow render I cannot achieve that. The test would succeed if this.myReference.current.childFunction(); wasn't called. Jest cannot know it because it only renders shallow and therefore throws an error.
I may be missing the full understanding. I wonder if someone has a Idea to test this function without using mount.
Take a look at the below code where I shallow render a component and then get the class instance and mock the required functions. Now when we call somefunction, we check if doSomethingOther has been called. (Assumption you are using jest+enzyme)
const wrapper = shallow(<Comp />);
const inst = wrapper.instance();
inst.myReference = {
current: {
childFunction: jest.fn()
}
}
inst.doSomethingOther = jest.fn();
inst.somefunction();
expect(inst.doSomethingOther).toHaveBeenCalled();

What's the proper way of passing a ref to a prop?

I'm trying to pass a ref of a component to another component. Since string refs are being deprecated I'm using callback refs.
So I have something similar to this:
<One ref={c => this.one = c}/>
<Two one={this.one}/>
The problem is that whenever I try to access this.props.one inside Two I get undefined.
I have even tried this on Two:
componentDidMount(){
setTimeout(()=>{
console.log(this.props.one);
},5000)
}
It seems the problem is that when the prop is created, the ref doesn't exist yet since it's created once One is mounted. But I don't know how to "refresh" the props on Two to get the ref to the mounted component.
So what's the proper way of passing a ref to another component?
Edit
Some users have suggested to encapsulate that logic in a higher component, which in itself renders those other child components.
The problem with that approach is that you can't create reusable logic and you have to repeat the same logic over and over in those encapsulating components.
Let's say you want to create a generic <Form> component which encapsulates the submit logic to your store, error checking, etc. And you do something like this:
<Form>
<Input/>
<Input/>
<Input/>
<Input/>
<SubmitButton/>
</Form>
In this example <Form> can't access the instances (and methods) of the children since this.props.children doesn't return those instances. It returns some list of pseudo components.
So how can you check if a certain <Input/> has detected a validation error without passing a ref?
You have to encapsulate those components in another component with the validation logic. For example in <UserForm>. But since each form is different the same logic has to be copied in <CategoryForm>, <GoupForm>, etc. This is terribly inefficient which is why I want to encapsulate the validation logic in <Form> and pass references of the <Input> components to <Form>.
In general the "ref" feature is an anti-pattern in React. It exists to enable side-effect driven development, however in order to benefit the most from the React way of programming you should try to avoid "refs" if possible.
As for your particular issue, passing a child a ref to it's sibling is a chicken vs. egg scenario. The ref callback is fired when the child is mounted, not during render which is why your example doesn't work. One thing you can try is pushing the ref into state and then reading from state into the other child. So:
<One ref={c => !this.state.one && this.setState({ one: c })}/>
<Two one={this.state.one}/>
Note: without the !this.state.one this will cause an infinite loop.
Here is a codepen example of this working (look at the console to see the sibling ref logged): http://codepen.io/anon/pen/pbqvRA
This is now much simpler using the new ref api (available since React 16 - thanks to perilandmishap for pointing that out).
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.oneRef = React.createRef();
}
render () {
return (
<React.Fragment>
<One ref={this.oneRef} />
<Two one={this.oneRef} />
</React.Fragment>
}
}
}
You would consume the prop in Two like:
this.props.one.current
A few things of note with this approach:
The ref will be an object with a current property. That property will be null until the element/component is mounted. Once it's mounted, it will be the instance of One. It should be safe to reference it once <Two /> is mounted.
Once the <One /> instance is unmounted, the current property on the ref returns to being null.
In general, if you need to pass a reference to something that may not be set at call time, you can pass a lambda instead:
<One ref={c => this.one = c}/>
<Two one={() => this.one}/>
and then reference it as
this.props.one()
If it has been set when you call it, you'll get a value. Before that, you'll get undefined (assuming it hasn't otherwise been initialized).
It bears noting that you won't necessarily re-render when it becomes available, and I would expect it to be undefined on the first render. This is something that using state to hold your reference does handle, but you won't get more than one re-render.
Given all that, I would recommend moving whatever code was using the ref to One in Two up into the component that is rendering One and Two, to avoid all the issues with both this strategy, and the one in #Carl Sverre's answer.

Reactjs - Get a component from anywhere in the app

I'd like to know if there is a way to get a component by using some type of id, or by type, similar as you would do in DOM manipulation. Something like:
var Avatar = React.createClass({
render: function () {
...
}
});
React.renderComponent(Avatar({id:'avatar'}), ...);
...
...
var avatar = React.getComponentById('avatar');
avatar.setProps({url = 'http://...'});
// or
var avatars = React.getComponentByType('Avatar');
if (avatars.length) {
avatars[0].setProps({url = 'http://...'});
}
I don't want to keep references of components instances...
setProps is something that you should use sparingly. In fact storing references to "rendered" components in general might indicate that you can structure your code differently. We also limit your uses of setProps to top level components.
var avatar = React.renderComponent(<Avatar .../>, node);
avatar.setProps({ foo: '1' });
is equivalent to this, which fits in a bit better with the declarative model:
React.renderComponent(<Avatar .../>, node);
React.renderComponent(<Avatar ... foo="1" />, node);
You could wrap that render up inside a function call so you could call it at will.
Sorry, there's no (publicly exposed) global registry of mounted React components. If you need to send messages to a component after mounting it, the best way is to save a reference to it. Iterating through the list of all Avatar components seems like the wrong solution to me anyway because it wrecks the composability aspect of components, where each parent component can specify its child's props and trust that outside forces won't change them -- changing this makes your page harder to reason about.
If you provide a jsfiddle of what you're trying to do, perhaps I can be of more help.

Categories

Resources