Enzyme Test Code Memory cleanup - reactjs

I have been looking at a lot of examples for testing react and redux applications using Enzyme and Jest, and there are very few that even mention code cleanup. When using shallow or mount do you not explicitly need to call unmount or detach to try to keep the memory bloat and runtime down?

There is a case where we will want to use detach to cleanup. Let's look at the simple shallow and mount case first.
Assigning to variables
This 'simple' case is where we simply assign the render to a var/const/let.
If we look at the (cut down) example of using Jest and Enzyme from the Enzyme Github.
describe('<MyComponent />', () => {
it('renders three <Foo /> components', () => {
const wrapper = shallow(<MyComponent />);
expect(wrapper.find(Foo)).to.have.lengthOf(3);
});
it('renders an `.icon-star`', () => {
const wrapper = shallow(<MyComponent />);
expect(wrapper.find('.icon-star')).to.have.lengthOf(1);
});
});
We can see the shallow method being called and the result being assigned to a const. The result is a ShallowWrapper object.
As const has a block scope when the execution leaves the block it is defined - in this case the test arrow function - the Javascript engine will automatically deallocate the ShallowWrapper memory.
It's because of this we don't need to worry about unmounting - this is only used to test specific Component lifecycle methods.
Attaching Components to DOM
We can also attach components to the DOM.
If we look at the test case automatically generated by the create-react-scripts.
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
This example doesn't use Enzyme, but it's the same concept as to when you use attachTo in the Enzyme mount function.
We can see our Component is being attached to a div in the document and then ReactDOM.unmountComponentAtNode being called to cleanup. This is actually what detach calls.
We need to do this cleanup because a reference to our rendered Component exists outside our block scope and therefore will not be deallocated when execution exits this block.

Related

Enzyme: Test children rendering failling with mount but passes with shallow

I'm using Enzyme + Jest to test some React components.
I have the following test:
describe('<Modal />', () => {
let wrapper;
it('should render children props', () => {
wrapper = shallow(<Modal />);
wrapper.setProps({
children: <div className='should-render'>This should be rendered</div>
});
expect(wrapper.find('.should-render').length).toEqual(1);
});
});
And it works just find. But, if I replace the shallow method from enzyme with mount the test fails (can't find a element with .should-render class).
Is this the expected behavior? I though that the difference between shallow and mount was the ability to access lifecycle methods but render worked the same.
Ok. So the problem was my lack of understanding of how mount works.
My Modal component has a state variable called show that prevents the element from mounting if it's set to false (I'm wrapping react-boostrap modal component, that has this beahavior). By default, this state variable it's false and since the children are being rendered in the body of the modal, no children were found because the element wasn't beign mounted.

Test if props are passed (Without using Enzyme)

I want to test if my props are passed, but without using Enzyme,
I tried to find documentation on the internet, but all the tutorials are with Enzyme.
describe('App Component', () => {
it('renders the Counter wrapper', () => {
const wrapper = shallow(<App />);
expect(wrapper.find(Counter)).to.have.length(1);
});
it('passes all props to Counter wrapper', () => {
const wrapper = shallow(<App />);
let counterWrapper = wrapper.find(Counter);
expect(counterWrapper.props().counter).to.equal(0);
wrapper.setState({ counter: -1 });
counterWrapper = wrapper.find(Counter);
expect(counterWrapper.props().counter).to.equal(-1);
});
Could someone help me? Can I do this with jest? Or I do need a third-party library like 'react-testing-library'?
Enzyme shallow is primarily intended for isolated unit tests like the one that was listed, only the implementation of tested unit is asserted. It offers some instrumentation (and also non-compliant behaviour) that doesn't exist in React itself.
react-testing-library is primarily intended for blackbox functional tests, the effects of units on resulting DOM are asserted.
React's own ReactTestUtils also offer some basic functionality, a subset of Enzyme and react-testing-library features, including shallow renderer for isolated tests.
It's possible to do isolated tests and assert the implementation without Enzyme, this results in boilerplate code. The approach isn't specific to testing library, it could be achieved with any renderer, including React's test renderer's snapshot testing. Everything (Counter component) but tested unit (App component) should be mocked with Jest, e.g.:
it('passes all props to Counter wrapper', async () => {
const CounterMock = jest.fn(() => 'Counter');
jest.mock('./counter-module', () => CounterMock);
const App = await import('./app-module');
// render <App/> and assert that the result is <div>Counter</div>
expect(CounterMock).toBeCalledWith({ counter: 0 });
});

How to test forwarded ref in React using test renderer or enzyme?

I use some amount of animations in my application and all animations depend on the refs (I am using GSAP). Most of the tested elements are located in other React components; so, I have set up forwardRef in my components to to pass the ref to the needed elements.
Now, I want to test these refs using Jest and Enzyme or React Test Renderer but I have not been able to achieve it with both the libraries. Here is my current implementation:
it('passes refs to the container component', () => {
const ref = React.createRef();
const div = document.createElement('div');
ReactDOM.render(<Row ref={ref} />, div);
const element = div.querySelector('div');
expect(element).toBe(ref.current);
});
Using ReactTestRenderer.create or enzyme.render to do a full rendering does not work because the refs are empty. Then, I found enzyme.mount function, which does something similar to ReactDOM.render; so, decided to use that:
it('passes refs to the container component', () => {
const ref = React.createRef();
const wrapper = mount(<Row ref={ref} />);
const div = wrapper.find('div').first().getDOMNode();
expect(div).toBe(ref.current);
});
This test does not pass because for some reason, the div returns HTMLElement while ref.current return something called WrapperComponent instead of returning the forwarded element.
Creating element and using ReactDOM to render and test the ref works fine but I would want to know if there is a way to use enzyme or react test renderer for this purpose (I am not a fan of using multiple libraries for rendering to test different functionalities).
The main reason I want to to test refs is because if a ref changes to another element in the component, all the animations that use the component's ref will break. So, I want to specify what elements are accessed when using refs.
Try to use ref.current.getDomNode().

enzyme test, context not rendering elements

I am using enzyme to write my tests, before adding a context to my component which is the consumer component in this case, the snapshot rendered is not showing any elements and every time i add a dive() or shallow() I got the same error
how can i access the elements while using context
const wrapper = shallow(
<MyComponent {...initialProps} store={mockStore(initialState)} />,
{context},
);
const contents = wrapper
.dive() // dive in connect
.dive() // dive in withLogss
.dive() // dive in Motion
.find("MyComponent")
.dive(); // dive in Host
expect(contents.context()).toEqual({myBoolean: true});
it("should render properly", () => {
expect(contents).toMatchSnapshot();
});
Result:
<ContextConsumer>
<Component />
</ContextConsumer>
Enzyme prior to v3.10.0 lacked support of React's new Context API.
From the v3.10.0 Changelog:
shallow: Support rendering and dive()ing createContext() providers and consumers (#1966)
Make sure you are using enzyme v3.10.0 or later and enzyme-adapter-react-16 v1.15.1 or later.

Are Enzyme / React shallow renders expensive?

We're having a discussion at work about Enzyme shallow renders and the time per test to re-run shallow on each test. Be it methods, clicks, selector lengths, etc., I'm suggesting that our tests might run faster if we shallow render the component one time before the tests run versus each time.
Are there any experts who can point out which way would be faster and if there are any pitfalls in either way? These examples are using the AVA runner (and slightly contrived for the sake of discussion).
For example, here's one way (A)...
import TagBox from '../TagBox';
const props = { toggleValue: sinon.spy() };
let wrapper = {};
test.before(t => {
wrapper = shallow(<TagBox />);
});
test('it should have two children', t => {
t.is(wrapper.children().length, 2);
});
test('it should safely set props', t => {
wrapper.setProps({...props});
t.is(wrapper.children().length, 2);
});
test('it should call when clicked', t => {
wrapper.setProps({...props});
wrapper.find({tagX : true}).last().simulate('click');
t.true(props.toggleValue.calledOnce);
});
And here's the other (B)...
import TagBox from '../TagBox';
test('it sets value to null ...', t => {
const props = {multiple: false};
const wrapper = shallow(<TagBox {...props} />);
t.is(wrapper.state('currentValue'), null);
});
test('it sets value to [] if multiple', t => {
const props = {multiple: true};
const wrapper = shallow(<TagBox {...props} />);
t.deepEqual(wrapper.state('currentValue'), []);
});
test('it does not use value if ...', t => {
const props = = {value: 3};
const wrapper = shallow(<TagBox {...props} />);
t.is(wrapper.state('currentValue'), null);
});
// etc. etc.
Notice that in test B, there is a new shallow wrapper for each test when essentially nothing has changed but props.
Over the course of 100 tests, what would you expect to be the difference in time to completion?
Also is there any chance shallow rendering once (test A) in the higher scope would pollute the test state?
Shallow renderer is designed to be fast, because it renders only single component. So, usually you will not get any performance troubles, when you create new component for each test.
Also, your example A can work incorrectly if TagBox component has inner state. That't why example B is more preferable way to write tests.
The shallow is probably not your problem here since it's designed to be the fastest way to render a component without cascading all of it's children renders.
You may consider changing your test running engine then, AVA is kinda slow compared to Jest for example. I did this change a year ago and it is a LOT faster. Jest also provides in it's base kit more useful stuff like mocking functions of example.
More here: https://facebook.github.io/jest/

Resources