Hardcoded expected string vs extracted variable in React shallow test - reactjs

I made two versions of the same simple test and I am looking for the better one. The only difference is that the first returns HTML code <div>::name::</div> and the second one - JSX <div>{name}</div>. I believe the goal of this kind of render test is to prove that the correct HTML is being returned from the render, so I believe the first approach is more accurate. Thoughts?
version 1:
const prop = {name: '::name::'}
const wrapper = shallow(<Component prop={prop} />)
expect(wrapper.equals(
<div>
<div>::name::</div>
</div>
)).toEqual(true)
version 2:
const name = ::name::
const wrapper = shallow(<Component prop={name: name} />)
expect(wrapper.equals(
<div>
<div>{name}</div>
</div>
)).toEqual(true)

First thumb rule in React unit testing - check only the conditionals.
What that means is, when you pass a prop and just render the prop in a div, it will be rendered. You don't have to test if the same value you pass is rendered in the div because that is the library behaviour and React is already tested to render props values properly.
Instead, your test cases should focus on conditionals and your logic. For example
const Example = ({ isFoo }) => {
return(
<div>{isFoo ? <Foo /> : <Bar />}</div>
);
};
Now this should be unit tested as
const wrapper = shallow(<Example isFoo={true} />);
expect(wrapper.contains('Foo')).toEqual(true);
expect(wrapper.contains('Bar')).toEqual(false);
Note: syntax might vary but you get the gist.

There is no big difference since
::name:: is still hardcoded
.equals() does not check if it's a reference to the same variable.
So there is still no way to validate if name is coming from props and not hard-coded equally to testing value.
It's better just update props in your test and ensure component is changed:
wrapper.setProps({prop: {name: 'anotherName'}});
expect(wrapper).toMatchSnapshot();
BTW you better rely on toMatchSnapshot() instead of comparing manually.

Related

Testing React Components that get state + setState properties passed by parent

How would you test React components that accept state hooks as properties?
For example
In the app, this ChildComponent is rendered like so:
<App>
<OtherComponent />
<div>
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
</div>
</App>
In my test, i'm trying to do this:
component = await render(
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
);
However I don't know how I would pass in these two properties selectedProperty and setSelectedProperty which in the app is handled by the parent component.
Thank you.
As a minimum example, fulfilling only the requirements you specified, this should work:
const selectedProperty = 'whatever';
const setSelectedProperty = () => {};
component = await render(
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
);
In case you need to verify that ChildComponent actually called setSelectedProperty, you can use a jest mock.
const selectedProperty = 'whatever';
const setSelectedProperty = jest.fn();
component = await render(
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
);
expect(setSelectedProperty).toHaveBeenCalled();
And of course, the whole of Jest function mocks are available to you (documented here: https://jestjs.io/docs/mock-functions).
------ Edit -------
I think you're actually asking: "How can I change state passed to a component in response to that component calling a set...?". Excellent question! You can either not (see more below) or check answers to this question: How to test a prop update on React component
The reason I would suggest to not to is because 1. your test is growing too much and 2. it's not necessary. Presumably you want to test two things:
When something happens, setSelectedProperty is called, and
The component renders properly for different values of selectedProperty.
There is no need to test those together; test them separately. If your component holds so much state that you feel you need to test them together, it may be that your component is too stateful. React should be as "functional" as possible and without side effects. If your component is becoming unmanageable and difficult to test in this regard, don't give up testing, redesign the component.

Advantages of rendering a component vs a function returning JSX [React]

I understand that, within your React component, it's always good to render a component rather than use a function that renders some JSX.
What I mean is,
Doing this :
export default function App() {
const [count, setCount] = useState(0);
const someRenderFunction = () => <p>Hey</p>;
return (
<div className="App">
{someRenderFunction()}
<button
onClick={() => {
setCount((c) => c + 1);
}}
>
Increment{" "}
</button>
<p>{count}</p>
</div>
);
}
is NOT encouraged. The render function should be exported into it's own Component, like this :
const HeyComponent = () => <p>Hey</p>
export default function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<HeyComponent />
<button
onClick={() => {
setCount((c) => c + 1);
}}
>
Increment{" "}
</button>
<p>{count}</p>
</div>
);
}
But I never really understood the benefits of this refactor. I tried placing checking the re-render behaviours, unmount behaviours. Still didn't see any difference.
Can anyone explain in detail why this refactor is necessary/beneficial ?
This is all about components, so components should be reusable and should follow the DRY principle, in your case that seems to be so simple and just as you said will prevent the someRenderFunction() to be re-rendered if there aren't any changes to that component in the virtual dom so the best practices are to use <X /> syntax always or for some cases const Y = <X /> also is more readable. testing is another reason to create more components and make them more decoupled. imagine here you need to pass props to your someRenderFunction() so you will lose the jsx feature for passing props as <X prop={PROP}/>.
The actual difference between the two is that the function returning JSX is not a component, so it does not have its own state. When you use useState in the function, the state change will cause the App to be re-rendered.
Two reasons why the second way is better than the first way are:
The function which defines the <p>Hey</p> JSX (named someRenderFunction in the first way, and HeyComponent in the second) is moved outside of the component App in the second way. As of now, there's no benefit, but if you later wanted to extend someRenderFunction/HeyComponent to have normal component lifecycle that can be hooked into, moving it outside of App would be essential. For example, if you wanted the function/component to keep track of its own state, the first way would cause someRenderFunction's state to get reset every time App renders. More info on this problem can be found here: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md, and here: https://stackoverflow.com/a/64211013/693855.
HeyComponent would be seen as a valid component whereas someRenderFunction would not, because component names need to start with a capital letter (this is mentioned here https://reactjs.org/docs/components-and-props.html#rendering-a-component in the Note at the bottom of the section). If you used hooks in your function, and it was not seen as a valid component by React, it would be breaking the "Rules of Hooks" set by React (https://reactjs.org/docs/hooks-rules.html#only-call-hooks-from-react-functions). I'm not sure if this would cause any bugs, but it is frowned upon by React -- if you have the "react-hooks/rules-of-hooks" ESLint rule enabled (from https://www.npmjs.com/package/eslint-plugin-react-hooks), you would see an error like this: ESLint: React Hook "useState" is called in function "someRenderFunction" that is neither a React function component nor a custom React Hook function.(react-hooks/rules-of-hooks)
The code which renders a component is followed as best practice.
Using Components we can pass state values to child component and can be used to display data. The same component can be re-used in other places.
If you have to just display a small function it can be used too if it shows constant information.

how to test third-party components library in react-testing-library

I have a react component, which imports a component from another third-party library (ex:Ant, or bootstrap ), and now I want to test this component onClick behavior.
However, it's difficult to getByTestId or getByText because the third-party library does not have thiese attribute or sometimes have no text in the html tag. How can I get the thrid-party dom and test it using react-testing-library ?
PS: some might say, we dont have to test bootstrap component because it's their business, but this will lower our test coverage.(ex: if we pass many callback functions props as event handlers to the a bootstrap component, we must test the callback to increase test coverage )
React Testing Library was developed, to my understanding, to write tests based on the user's experience of the elements being rendered. With third party components this can be cumbersome at times.
I think there are a few approaches to take:
Review the React Testing Library cheatsheet and ask yourself the primary way the user understands the element. Maybe in this case it is by the role of the element? I.e. does your third party component hold a aria property dictating the role for that component?
Example: app uses Buttons from bootstrap
const App = () => {
const [state, setState] = useState(0)
const increase = () => {
const newState = state + 1
setState(newState)
}
const decrease = () => {
const newState = state - 1
setState(newState)
}
return (
<div>
<div className="App">
<Button variant="primary" size="lg" onClick={increase}></Button>{' '}
<Button variant="primary" size="lg" onClick={decrease}></Button>{' '}
</div>
<div className="number">
{state}
</div>
</div>
)
}
I purposefully did not provide text for the buttons but I can still find those buttons, here is the test:
it('buttons increase and decrease displayed value', () => {
const component = render(<App/>)
const buttons = component.getAllByRole('button')
for (let i = 0; i < buttons.length; i++){
fireEvent.click(buttons[i])
}
expect(component.findByText('0')).toBeTruthy()
})
Even though I couldn't set a testId or find by text, I could still gather an array of nodes that play the role of button. From there it is a matter of searching through the array to find which button you need. And to my pleasure, the test passes but this is a silly test because the buttons have their own logic so I should separate them into separate tests as such:
it('increases and decreases button and displays accurate value', () => {
const component = render(<App/>)
const buttons = component.getAllByRole('button')
fireEvent.click(buttons[0])
expect(component.findByText('1')).toBeTruthy()
})
If needed consider wrapping the third party component in a div, provide a data-testid for that data, then access the first, sibling, last child nodes that are children to that div. I've often used this to navigate to where the third party component node exists in the dom:
<div data-testid="test">
<SomeComponent onChange={()=> {}}/>
</div>
Since some of the react testing library queries return a dom node, we can find this component with:
const findBootstrapComp = component.getByTestId('test').firstChild
What we should probably avoid as much as possible, but maybe your case is allowable given what can be access with the queries provided, you can query the container node manually by some attribute:
const { container } = render()
const button = container.querySelector('button')
Long story short, React Testing Library provides plenty of tools to query for what you need. If that third party component is rendering, then there are probably attributes for that element that you can query. The above is just from personal experience, each project is different. May even consider pulling out those callbacks, isolating them and then testing the logic since you're more concerned about the logic from the sounds of it.
Also, I agree with your sentiment on Boostrap testing their own components. You're not actually looking to test a third party component though, your looking to test some functionality or logic you have written that is being accessed by a third party component. Or maybe you want to pull the css of that element and confirm that it hasn't switched at some point during development. Whatever it may be, I hope the above helps in some way. If anything, create a component in a test file using the third party components and use the debug() api to take a look at the elements to see what they have that you may query for.
helpful links:
React Testing Library cheatsheet
Dom Navigation
Node interface

In a React/Jest unit test, how do you simulate an event prop being called by a mocked component?

Suppose I have a React component that is a Login form with a Username field. If the user sets the Username to a blank string, the component shows an error message.
Here's the code:
const LoginForm = () => {
const [errorMessage, setErrorMessage] = useState(''),
handleOnChangeUsername = (newUsername: string) => {
if (newUsername.trim() === '') {
setErrorMessage('Username is required.');
} else {
setErrorMessage('');
}
};
return (
<form>
<UsernameField onChange={handleOnChangeUsername} />
{errorMessage && <Error message={errorMessage} />}
</form>
);
};
I want to unit test this component.
So I want to write a test that verifies that when the user enters a blank username, then the error message: 'Username is required' is displayed underneath.
In order to write this unit-test, I assume that I need to do the following:
Mount the LoginForm component, providing a mocked UsernameField component.
Get the mocked UsernameField component to call its onChange prop, passing in a blank string.
Assert that the LoginForm component is now rendering an Error component (i.e. that the code in handleOnChangeUsername actually ran and produced the correct side-effect).
The mocking part is easy – I just call jest.mock(...), passing in the path to the module and a mock implementation.
The tricky bit is Step 2. How do I make my unit test cause the mock to call one of its own props, to simulate the onChange being called with an empty string?
It seems like there would have to be some channel by which the unit test code could communicate with the internal props of the mocked component.
Is there a simple and/or idiomatic way to do this in a React/Jest unit test?
Note: This question assumes a mockist style. I know that some developers might say that I should simply trigger the Username onChange directly, by manipulating the DOM element that the Username component renders. I already know how to do that. But I want to follow a mockist style and completely mock the Username component.
Ok, I seem to have found at least one way of accessing the props. It requires Enzyme, though; still not sure how to do this without Enzyme.
Enzyme's wrapper includes a props() method, which returns the props of the wrapped component.
So I can write this:
const root = mount(<LoginForm />);
root.find("UsernameField").props().onChange("");
expect(root.find("Error").exists()).toBeTruthy();

What is the difference between simulating the actions & calling them manually in jest?

What is the difference between simulating the actions & calling them manually?
I have mounted the component using enzyme.
const wrapper = mount(
<Provider store={store}>
<CreateNavItemForm />
</Provider>,
)
const formWrapper = wrapper.find('CreateNavItemForm').first()
What is the difference and which approach is better?
const title = 'title'
1st:
formWrapper
.find('input')
.at(0)
.props()
.onChange(title)
2nd:
formWrapper
.find('input')
.at(0)
.simulate('change', { target: { value: title } })
A good question, there is a difference, when you checking using simulate, you are mimic more real scenario.
Furthermore, you are checking that your component is wired properly: for example, consider that this is your component:
const SpecialInput = ({onChange}) => (<input name />);
This component is not using the onChange at all, your test with prop usage will pass, but in real scenario it won't work as expected.
For addition, when you are testing using prop usage, that means that you are aware of the inner implementation of the component, so, if in the future you will change the implementation but not the functionality you will need to change the test, which is a smell of bad test.

Resources