How do I test that a child component is rendered? - reactjs

In enzyme you can check for the existence of child component like this:
expect(wrapper.find(ChildComponent)).toHaveLength(1)
What is the equivalent to this test in react testing library? All the online examples I find just cover very simple tests looking for dom elements - none include examples of that render child components. How do you find a child component?

You shouldn't check if your child component is rendered or not, because it's testing implementation details (which testing library doesn't encourage you to do).
You can check some text from your child component is rendered or you can give data-testid to your wrapper element in child and then use .toBeInTheDocument from #testing-library/jest-dom
expect(getByText(/some text/i)).toBeInTheDocument();
or
expect(getByTestId('your-test-id')).toBeInTheDocument();
Updated: Example
// Child Component
function ChildComponent() {
return <div>Child Element</div>;
};
// Parent
export default function Parent() {
return (
<div className="App">
<ChildComponent />
</div>
);
}
Test:
import { render } from "#testing-library/react";
import "#testing-library/jest-dom/extend-expect";
import Parent from "./App";
test("test child component", () => {
const { getByText } = render(<Parent />);
expect(getByText(/child element/i)).toBeInTheDocument();
});

You can use #testing-library/jest-dom library.
Component:
<div role="root">
<div data-testid="parent">
<div data-testid="child">
content
</div>
</div>
</div>
Test:
import '#testing-library/jest-dom'
import {render} from '#testing-library/react';
describe('My component', () => {
test('should render component2', () => {
const { getByRole, getByTestId } = render(<Component/>);
const root = getByRole('root');
const parent = getByTestId('parent');
const child = getByTestId('child');
expect(root).toContainElement(parent);
expect(parent).toContainElement(child);
expect(child).not.toContainElement(parent); // Pass
expect(child).toContainElement(parent); // Throw error!
});
});
Another solution is to use within function from #testing-library/react library:
import { within } from '#testing-library/react';
...
expect(within(parent).queryByTestId('child')).not.toBeNull();

I used React Test Renderer for that purpose:
import TestRenderer from 'react-test-renderer';
import ItemList from 'components/ItemList';
import LoadingIndicator from 'components/LoadingIndicator';
test('renders loading indication', () => {
const testRenderer = TestRenderer.create(
<ItemList items={[]} isLoading={true}/>
);
const testInstance = testRenderer.root;
testInstance.findByType(LoadingIndicator);
});
I don't think that it's "testing implementation details", quite the opposite - LoadingIndicator component can be modified without need to fix the test case.

since query* return null if element is not found you may just
expect(queryByTestId('test-id-you-provided')).toEqual(null);
expect(queryByTestId('some-other-test-id-you-provided')).not.toEqual(null);
Also getBy* throws if element is not find. So next one should work as well
getByTestId('test-id-you-provided');

I agree with everyone who said, that checking for either text or a test-id inside a child component is testing implementation details.
But we could use mocking, to get rid of the implementation details of the child component.
The code to test:
import { ChildComponent } from 'child-from-somewhere';
export function Parent() {
return (
<div className="App">
<ChildComponent />
</div>
);
}
The test code that checks, if ChildComponent was rendered:
import { render } from "#testing-library/react";
import React from "react";
import { Parent } from "./parent";
jest.mock("child-from-somewhere", () => ({
ChildComponent: () => <div data-testid="ChildComponent">ChildComponent</div>,
}));
describe("ChildComponent component", () => {
it("should be in the document", () => {
const { getByTestId } = render(<Parent />);
expect(getByTestId("ChildComponent")).toBeInTheDocument();
});
});
That way, the test stays independent of changes that are made inside of ChildComponent.

Related

How to check if a component disappears after click using jest & enzyme?

I have a component,
function TestComponent() {
const [visible, setVisible] = useState(true);
return (
<React.Fragment>
{visible && <Container>
I'm Visible
<button onClick={() => setVisible(false)}>
click to close
</button>
</Container>}
</React.Fragment>
);
}
I'm trying to test that on clicking the button the component should be invisible.
And I have following test case to test that,
test('Random Test', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toBeFalsy();
});
Doesnt seem to work,
Getting error,
expect(received).toBeFalsy()
Received: {}
Any help would be appreciated?
I have a sneaky suspicion that this isnt the way to check if component is hidden. Would also appreciate if anyone could tell a better way.
Update #1:
expect(randomComponent.render().text()).toContain('I\'m Visible');
randomComponent.find('button').simulate('click');
expect(randomComponent.render().text()).toContain('');
Using the above testcases seem to work. Still looking for a better way.
Since .find() will always return an instance of ShallowWrapper class even if there is no node matched. See source code of .find() and .wrap(). It will NOT return a falsy value (null, undefined), so .toBeFalsy() assertion will always fail.
Use .exists([selector]) => Boolean
Returns whether or not any nodes exist in the wrapper. Or, if a selector is passed in, whether that selector has any matches in the wrapper.
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container').exists()).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container').exists()).toBeFalsy();
});
});
Use .toHaveLength(number) matcher
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toHaveLength(1);
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toHaveLength(0);
});
});

Problems testing a Redux + React app with enzyme:

I have this component
import React, { useEffect } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { CircularProgress } from '#material-ui/core';
import { loadPhones } from './redux/actions/actions.js';
import TablePhones from './Table.js';
const mapStateToProps = (state) => state;
function mapDispatchToProps(dispatch) {
return {
loadPhones: () => {
dispatch(loadPhones());
},
};
}
export function App(props) {
useEffect(() => {
props.loadPhones();
}, []);
if (props.phones.data) {
return (
<div className="App">
<div className="introductoryNav">Phones</div>
<TablePhones phones={props.phones.data} />
</div>
);
}
return (
<div className="gridLoadingContainer">
<CircularProgress color="secondary" iconStyle="width: 1000, height:1000" />
<p className="loadingText1">Loading...</p>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
For whom ive written
import React from 'react';
import { render } from '#testing-library/react';
import { Provider } from "react-redux";
import App from './App';
import { shallow, mount } from "enzyme";
import configureMockStore from "redux-mock-store";
const mockStore = configureMockStore();
const store = mockStore({});
describe('App comp testing', () => {
it("should render without throwing an error", () => {
const app = mount(
<Provider store={store}>
<App />
</Provider>
).dive()
expect(app.find('.introductoryNav').text()).toContain("Phones");
});
})
But that test keeps failing
ypeError: Cannot read property 'data' of undefined
I also tried importing App as {App} instead and using shallow testing, but no luck. It gives the same erros, so im left without access to the context, and I cant keep doing my tests
How can I solve this?
You could use the non-default export of your component here and shallow render test if you pass your component the props and don't try to mock the store (if I recall correctly).
I was thinking something like this might work, tesing the "pure" non-store connected version of the component. This seems to be a popular answer for this question as this was asked (in a different way) before here:
import React from 'react';
import { App } from './App';
import { shallow } from "enzyme";
// useful function that is reusable for desturcturing the returned
// wrapper and object of props inside of beforeAll etc...
const setupFunc = overrideProps => {
const props = {
phones: {
...phones, // replace with a mock example of a render of props.phones
data: {
...phoneData // replace with a mock example of a render of props.phones.data
},
},
loadPhones: jest.fn()
};
const wrapper = shallow(<App {...props} />);
return {
wrapper,
props
};
};
// this is just the way I personally write my inital describe, I find it the easiest way
// to describe the component being rendered. (alot of the things below will be opinios on test improvements as well).
describe('<App />', () => {
describe('When the component has intially rendered' () => {
beforeAll(() => {
const { props } = setupFunc();
});
it ('should call loadPhones after the component has initially rendered, () => {
expect(props.loadPhones).toHaveBeenCalled();
});
});
describe('When it renders WITH props present', () => {
// we should use describes to section our tests as per how the code is written
// 1. when it renders with the props present in the component
// 2. when it renders without the props
beforeAll(() => {
const { wrapper, props } = setupFunc();
});
// "render without throwing an error" sounds quite broad or more like
// how you would "describe" how it rendered before testing something
// inside of the render. We want to have our "it" represent what we're
// actually testing; that introductoryNave has rendered with text.
it("should render an introductoryNav with text", () => {
// toContain is a bit broad, toBe would be more specific
expect(wrapper.find('.introductoryNav').text()).toBe("Phones");
});
it("should render a TablePhones component with data from props", () => {
// iirc toEqual should work here, you might need toStrictEqual though.
expect(wrapper.find('TablePhones').prop('phones')).toEqual(props.phones);
});
});
describe('When it renders WITHOUT props present', () => {
it("should render with some loading components", () => {
expect(wrapper.find('.gridLoadingContainer').exists()).toBeTruthy();
expect(wrapper.find('CircularProgress').exists()).toBeTruthy();
expect(wrapper.find('.loadingText1').exists()).toBeTruthy();
});
});
});

How do you use Enzyme to check for changes to a React Hooks component after onClick?

I am trying to write a simple integration test in my 100% React Hooks (React v16.12) project with Enzyme (v3.10), Jest (v24.0) and TypeScript where if I click a button component in my App container, another component displaying a counter will go up by one. The current value of the counter is stored in the state of the App container (see snippets below).
Basically, I mount the App component to render its children, then try to simulate a click on the button with Enzyme and check the props of the counter display component to see if its value has gone up. But nothing happens. Not only does the onClick handler not get called but I don't seem to be able to retrieve the value prop I pass to the PaperResource component. So basically I can't test the counter display changes when I click on the button in my Enzyme integration test! The test asserts that the value prop goes from 0 to 1, but this assertion fails without an error per seenter code here. Is this because Enzyme support for Hooks is still not there yet or am I doing something daft here? When I run the app on my browser, everything works as expected.
Here's my integration test
import React from 'react';
import App from './App';
import { mount, ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import MakePaperButton from './components/MakePaperButton';
import PaperResource from './components/PaperResource';
describe('App', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
act(() => {
wrapper = mount(<App />);
});
});
describe('when make paper button is clicked', () => {
beforeEach(() => {
act(() => {
wrapper.find('.make-paper__button').simulate('click');
});
});
it('should increase paper resource', () => {
expect(wrapper.find('.resources__paper').prop('value')).toEqual(1);
});
});
});
And here is my React code
import React, { useState } from 'react';
import './App.scss';
import MakePaperButton from './components/MakePaperButton';
import PaperResource from './components/PaperResource';
const App: React.FC = () => {
const [ resources, setResources ] = useState({
paper: 0,
});
const handleMakePaperButtonClick = () => {
setResources({
...resources,
paper: resources.paper + 1,
});
};
return (
<div className="App">
<MakePaperButton onClick={handleMakePaperButtonClick} />
<div className="resources">
<PaperResource value={resources.paper} />
</div>
</div>
);
}
export default App;
My components are very simple
// PaperResource.tsx
import React from 'react';
export default (props: { value: number }) => (
<div className="resources__paper">
<span>Paper: {props.value}</span>
</div>
);
// MakePaperButton.tsx
import React from 'react';
export default (props: { onClick: () => void }) => (
<div className="make-paper__button">
<button onClick={props.onClick}>Make Paper</button>
</div>
);
The only solution I've found so far is wrapping the expect statement in a setTimeout().
it('should increase paper resource', () => {
setTimeout(() => {
expect(wrapper.find('.resources__paper').prop('value')).toEqual(1);
}, 0);
});

Is it possible to wrap each individual test in a wrapper component?

I am using material-UI components in my React components. Therefore I will need to apply the <MuiThemeProvider></MuiThemeProvider> component around all my components in my tests.
My components are located in individual folders:
./src/components/Header/Header.tsx
./src/components/Header/Header.test.ts
./src/components/Header/...
./src/components/Footer/Footer.tsx
./src/components/Footer/Footer.test.ts
./src/components/Footer/...
// etc.
A test would have to look like the following:
import React from 'react';
import { render } from '#testing-library/react';
import Header from './Header';
it('matches snapshot', () => {
const container = render(
// This theme provider is necessary since my components depend on it.
// But rather don't want to include this in all my components.
<MuiThemeProvider theme={theme}>
<Header />
</MuiThemeProvider>
);
expect(container).toMatchSnapshot();
});
But now I'd have to define the MuiThemeProvider in each of my tests.
Is it possible to do this once for all my tests?
Found it: https://testing-library.com/docs/react-testing-library/setup
Turns out it's a react-testing-library thing.
You can modify the render function to wrap a component (provider) in the render method.
Simply change the import of the render function:
Create the function containing with the 'wrapper' component:
// test-utils.js
import { render } from '#testing-library/react'
import { ThemeProvider } from 'my-ui-lib'
import { TranslationProvider } from 'my-i18n-lib'
import defaultStrings from 'i18n/en-x-default'
const AllTheProviders = ({ children }) => {
return (
<ThemeProvider theme="light"> // here it is
<TranslationProvider messages={defaultStrings}>
{children}
</TranslationProvider>
</ThemeProvider>
)
}
const customRender = (ui, options) =>
render(ui, { wrapper: AllTheProviders, ...options })
// re-export everything
export * from '#testing-library/react'
// override render method
export { customRender as render }
And then start using it like this:
// my-component.test.js
- import { render, fireEvent } from '#testing-library/react';
+ import { render, fireEvent } from '../test-utils';
You can mock your higher-order component using jest.mock.
It can something like following
jest.mock("hoc-module", () => {
return {
hocFunction: function(hocConf) {
return function(component) {
component.defaultProps = {
...component.defaultProps,
mockPropKey: mockPropValue
};
return component;
};
}
};
});
P.S: I wrote the above code on the fly.

Prevent enzyme shallow rendering on private component

How do I prevent shallow rendering on private component with enzyme?
Here is a component example:
// foo.jsx
import React from 'react';
// Private component
const FooSubtitle = ({subtitle}) => {
if (!subtitle) return null;
return <div className="foo__subtitle">{subtitle}</div>;
};
// Public component
const Foo = ({title, subtitle}) => (
<div className="foo">
<div className="foo__title">{title}</div>
<FooSubtitle subtitle={subtitle} />
</div>
);
export default Foo;
Here is my specification:
// foo.spec.js
import React from 'react';
import {shallow} from 'enzyme';
import Foo from './foo.jsx';
describe('Foo', () => {
it('should render a subtitle', () => {
const wrapper = shallow(<Foo title="my title" subtitle="my subtitle" />);
// This test doesn't work, so I cannot test the render of my component
expect(wrapper.find('.foo__subtitle').length).toBe(1);
// This one works, but it is not relevant
expect(wrapper.find('FooSubtitle').length).toBe(1);
});
});
Any idea?
Thanks a lot.
Shallow rendering is useful to constrain yourself to testing a
component as a unit, and to ensure that your tests aren't indirectly
asserting on behavior of child components.
I think you are trying to do what shallow tries to avoid ^^.
You can unit test the private component directly or use render :
expect(wrapper.find(Foo).render().find('.foo__subtitle')).to.have.length(1);
as explaned here : https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/render.md
But in both cases you'll need to export your Component and I must admit I had an error testing it with your component. :/
In this case (and in generally) your private component is just a function, use it as a function in the render of your public component and you will be able to test its render with the shallow wrapper.
<div className="foo">
<div className="foo__title">{title}</div>
{FooSubtitle({subtitle})}
</div>
Otherwise, I'm not sure it's a good idea to have complex private components...
You have to export your private component,
export const FooSubtitle = ...
Now, you are able to test it apart with all its prop variants.
Then you can test the presence of FooSubtitle, with particular props, in the render of Foo component as usual and nothing more.
If you have private components, and you want to test their implementation, you should:
Have at least enzyme v2.5
Look at the Enzyme .dive() API: Shallow render a non-DOM child of the current wrapper
Here is a working example:
// foo.jsx
import React from 'react';
// Private component
const FooSubtitle = ({subtitle}) => {
if (!subtitle) return null;
return <div className="foo__subtitle">{subtitle}</div>;
};
// Public component
const Foo = ({title, subtitle}) => (
<div className="foo">
<div className="foo__title">{title}</div>
<FooSubtitle subtitle={subtitle} />
</div>
);
export default Foo;
// foo.spec.js
import React from 'react';
import {shallow} from 'enzyme';
import Foo from './foo.jsx';
describe('Foo', () => {
it('should render a subtitle', () => {
const wrapper = shallow(<Foo title="my title" subtitle="my subtitle" />);
// This test works, but it is not relevant
expect(wrapper.find('FooSubtitle').length).toBe(1);
// This one need the `dive()` API to work
expect(wrapper.find('FooSubtitle').dive().find('.foo__subtitle').length).toBe(1);
});
});

Resources