React Enzyme Test - DOM manipulation with componentDidMount - reactjs

I am manipulating the DOM with componentDidMount based on element positions. It works perfectly in React, but I would like to create an unit test for that.
Basically, I have a container with a bunch of children elements and I want to add a class for children that are wrapped (not in the first row). I do that logic in componentDidMount.
How can I test with enzyme unit tests?
I thought to use mount but it does not result. Any idea?
class Container extends Component {
componentDidMount () {
this.updateChildren()
}
updateChildren () {
// logic to add a class if element position is different than 1st line
// calls render again with state update
}
...
}
Unit test:
import React from 'react'
import { mount } from 'enzyme'
import Container from '../Container'
describe('<Container />', () => {
it('renders and hides second row elements'), () => {
const container = mount(
<div style={{ width: 200 }}>
<Container> ...childrens </Container>
</div>
)
expect(wrapper.render()).toMatchSnapshot()
})
})

ComponentDidMount is just like a method in your class.
So, you can directly call the method in your unit test this way
wrapper = shallow(<Your Component />);
// call the method
wrapper.instance().componentDidMount()
You can write a mock implementation for that method if you need.

Related

React testing: test that component renders child component

I have a react component that renders a bunch of child components.
I am trying to test that this component indeeds renders these components.
I rather avoid Enzyme, so I am trying to use #testing-library/react and react-test-renderer.
I seem to be be able to get the child component via findByType, but I can't find any assertion in the library to check the element is rendered. I've tried checking for nullity but that doesn't work when the component is not there.
I don't want to use react-test-renderer's getByRole because that would be testing the role of the root element of the child component, and I think that belongs to the child component's test. I just want to test the child components are instantiated.
Am I approching this the wrong way? Is it not general practice to test a parent renders its children correctly? Should I be using snapshot testing instead?
App.tsx
...
return (
<Navbar className={styles.navbar} />
<Header />
<Footer />
);
App.test.ts
import { render, screen } from '#testing-library/react';
import Footer from './footer/Footer';
import Navbar from './navbar/Navbar';
describe('App', () => {
let component;
let instance;
beforeEach(() => {
// render(<App />); // Unsure if I should use react-testing-lib or react-test-renderer
component = renderer.create(<App />);
instance = component.root;
});
it('should render a navbar', () => {
expect(instance.findByType(Navbar)).not.toBeNull; ??
expect(instance.findByType('section')).toBeNull; // Just playing with the null assertion here, this causes the test to fail
});
it('should define the footer', () => {
expect(instance.findByType(Footer)).not.toBeNull; ??
});
});

React/Mobx: Integration Tests with Stores Injected into Child Components

We are trying to write out unit/integration tests for all of our existing React components. We are currently using React with Mobx 4, with tests written mostly with react-testing-library/jest. We did use Enzyme in some areas as well to make use of shallow rendering.
Our issue is that as we get to some of our 'pages', or container components, we are getting errors such as "MobX injector: Store 'teamStore' is not available! Make sure it is provided by some Provider"
We've done a bit of digging but couldn't find anything in our searches of similar issues for reference. We do know that this is caused by child components that have stores injected into them directly, and that are called into our container/page.
My question is: Is there any way within the testing frameworks to pass down mock stores created in our container components down to child components? Obviously if we passed the store as a prop from the parent to the child, that solves the issue, but we are trying to avoid modifying the components themselves in any way.
If the above is not possible, do we have any other options without refactoring components to pass down stores as needed rather than injecting directly into child components?
import React, { Component } from "react";
import { inject, observer } from "mobx-react";
import { Container, Grid, Segment } from "semantic-ui-react";
import ChildComp from "../../components/ChildComp";
#inject("userStore")
#observer
class ParentComponent extends Component {
render() {
return (
<Container className="parent">
<Segment basic>
<h1>Hello</h1>
<ChildComp />
</Segment>
</Container>
);
}
}
export default ParentComponent;
import React, { Component } from "react";
import { inject, observer } from "mobx-react";
import { Container, Grid, Segment } from "semantic-ui-react";
#inject("teamStore")
#observer
class ChildComp extends Component {
render() {
return (
<Segment basic>
<p>How can I help you?</p>
</Segment>
);
}
}
export default ChildComp;
Using jest you can mock parts of mobx to provide your own mock store so instead of running the real inject function you can provide your own inject function instead.
Using that custom inject function you can return a fake store (which needs to match the same interface as the original store).
If you want to pre populate the store with values by importing the mock you created (jest doesn't allow variables on the module/global scope to be used when using jest.mock)
Here is an example code that achieves this (this is untested code written right here on stackoverflow so might needs some tweaks to get right).
jest.mock('mobx-react', () => {
// get the original reference to mobx-react
const originalMobx = require.requireActual('mobx-react');
// create your fake stores, they should have the same interface as the real store
const mockStores = {
userStore: new UserStore()
};
return {
...originalMobx, // allow to import the original properties in react-mobx
// override the inject decorator to instead return the fake store as a prop
inject: (injectName) => (component) => (props) => {
// render the real component with the additional prop
return react.createElement(component, {...props, [injectName]: mockStores[injectName] })
},
mockStores // Allows access afterwards via import e.g import { mockStores } from 'mobx-react'
}
});
Once you mocked the mobx-react inject function you can reference the store to pre populate the values by:
import { mockStores } from 'mobx-react';
test('my test', () => {
mockStores.userStore.clearUsers();
// render the component here
})
There is also an alternative solution where you can just wrap the tested component with Provider from mobx-react and supply fake stores.
so the test will initialize them beforehand and pass the down the context.
e.g
test('my comp', () => {
const userStore = new UserStore();
const component = shallow(
<Provider userStore={userStore}>
<MyComponent />
</Provider>
)
});

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

How do I test React-Responsive components with Enzyme?

I am using the library React-Reponsive.
https://github.com/contra/react-responsive
I am struggling to figure out how to test components that are nested in React-Responsive Media Query Components:
export default class AppContainer extends React.Component {
render(){
return(
<MediaQuery minDeviceWidth={750}>
<Header />
</MediaQuery>
);
}
}
-
describe("AppContainer", () => {
let App;
let wrapper;
beforeEach(() => {
wrapper = mount(<Provider store={store}><AppContainer location={location} /></Provider>);
App = wrapper.find(AppContainer);
});
it('to have a <Header /> component', () => {
console.log(App.debug());
expect(App.find(Header)).to.have.length(1);
});
}
The test result:
1) AppContainer to have a <Header /> component:
AssertionError: expected { Object (component, root, ...) } to have a length of 1 but got 0
The relevant part of the output of the console.log is:
<MediaQuery minDeviceWidth={750} values={{...}} />
Indicating that Header is indeed not appearing in the render tree. However if I remove the MediaQuery and make Header a direct child of AppContainer the test passes.
I imagine this is not a bug as I'm very new to Enzyme and testing components in general. Any help or examples would be appreciated.
Please note: The other tests I have on this component are passing fine. I am confident that the imports and setup are all correct.
Issue was that Media Query is looking for window.matchMedia which with jsdom is undefined.
In this case I needed to use the Server Side Rendering implementation. However this would require a static value for width, which breaks the responsiveness.
My solution is to set a global variable on the test virtual DOM.
window.testMediaQueryValues = {width:740};
Then MediaQuery can access them if they are there:
<MediaQuery maxWidth={smallViewMaxWidth} values={window.testMediaQueryValues}>
In the case when the variable is not set, the null values are ignored and the Component renders as usual.
Big thanks to #Contra for his help and super library
What worked for me was adding a mocked react-responsive component using a __mocks__ directory. Basically create the following file in the directory structure:
-your-component
--component-using-media-query.js
--__mocks__
---react-responsive.js
Then you can mock out the MediaQuery component in the react-responsive.js file.
const MediaQuery = ({ children }) => children;
export default MediaQuery;
I was able to make it work using react-responsive v7.0.0.
Given:
<MediaQuery minDeviceWidth={750}>
<Header />
</MediaQuery>
The following works:
import { Context as ResponsiveContext } from 'react-responsive'
import { mount } from 'enzyme'
const wrappingComponent = ResponsiveContext.Provider
const wrappingComponentProps = { value: { width: 750 } }
const mountProps = { wrappingComponent, wrappingComponentProps }
const wrapper = mount(<AppContainer />, mountProps)
Try mocking the react-responsive dependency in your unit test. Working with Webpack, I'm using inject-loader for injecting test dependencies to modules. You can import your component using "inject-loader" and pass it which dependencies you want to overwrite.
Example:
import YourComponentInjector from 'inject-loader!../YourComponent.jsx';
and then
class MediaQueryDesktopMock extends React.Component {
render() {
const {minDeviceWidth, children} = this.props;
if(minDeviceWidth === 765) {
return (<div>{children}</div>)
}
return <span/>;
}
}
let YourComponentMock = YourComponentInjector({
'react-responsive': MediaQueryDesktopMock
});
you can then test for specific media queries

Enzyme/Mocha: How to test a react component function by firing an onChange event from a child component

I'm using enzyme/mocha to test my react component.
I have a parent component which I am testing.
let wrapper = mount(<Parent />);
and this parent has a child component in it's render function
render: function() {
<Child onChange={this.foo} />
},
foo: function() {
console.log("I was called");
}
I would like the child's onChange function to fire so that I can test my parents foo function.
So far, I have found no way to do this - I've read about sinon and stubbing but that's mostly about intercepting functions and not firing them.
The following test
shallow(<Parent />).instance().foo();
is a weak test because it doesn't test the line of code connecting my child and parent, and in case I haven't written a unit test for my child, it doesn't test the child's onChange functionality either. IMHO - If breaking up my components to parents/children means less testability - then something is wrong with this framework
Any help will be appreciated, thanks
This is something that I do in a lot of my tests. The method that I've found that works best for me is to manually invoke the child component's onChange handler, and make your assertions based on the behavior you expect to happen as a result.
So let's say you have a Parent component that looks like this:
import React from 'react';
import Child from './child';
export default class extends React.Component {
render() {
return (
<div>
<Child onChange={this.foo} />
</div>
);
}
foo() {
console.log('bar');
}
}
The onChange prop passed to child will log the string 'bar' when invoked. This is the behavior we want to test. To do this, we'll need to take the following steps:
Stub console.log using your mocking library of choice (I'll be using Sinon for this example)
Create a shallow instance of the Parent component, and obtain a reference to its Child.
Manually invoke the Child's onChange prop.
Assert that console.log was called once, and with a single argument: 'bar'
And here is how I would do that (using mocha and chai):
import Foo from './foo';
import React from 'react';
import {shallow} from 'enzyme';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chai, {expect} from 'chai';
describe('Foo', () => {
let renderedElement;
function renderComponent() {
const componentElement = React.createElement(Foo);
renderedElement = shallow(componentElement);
}
before(() => {
chai.use(sinonChai);
});
it('should log the string "bar" when the child component is changed', () => {
//step 1
sinon.stub(console, 'log');
//step 2
renderComponent();
const childComponent = renderedElement.props().children;
//step 3
childComponent.props.onChange();
//step 4
expect(console.log).to.have.callCount(1);
expect(console.log).to.be.calledWith('bar');
//clean up
console.log.restore();
});
});
The reason I like this approach is because it's testing component behavior as opposed to simply testing that it was passed a function as a prop that happens to be equal to another function.
Try breaking your tests into parts. For example...
Firstly, test that the expected function was passed to your child component:
import { shallow } from 'enzyme';
const actual = shallow(<Parent />);
const expected = <Child onChange={actual.instance().foo} />
expect(actual.matchesElement(expected)).true;
I like to use the simple matchesElement approach above when there is little being rendered in the Component I am testing, but you could also use a find selector to find the Child instance and then test it.
Then test your foo function seperately:
import { shallow } from 'enzyme';
const actual = shallow(<Parent />).instance().foo();
const expected = 'expected return of foo'
expect(actual).equals(expected);
You can test your Child component separately and how it handles it's onChange prop.
Some of the enzyme API's being used here:
.instance()
.matchesElement(node)
Also look at:
.containsMatchingElement(node)
.find(selector)

Resources