enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.95k stars 2.01k forks source link

How can i test a dynamically/lazy loaded component on jest #2212

Open sahithikol opened 5 years ago

sahithikol commented 5 years ago

How can i test a dyanmically/lazy loaded component on jest I have a test component which loads as

import React, { lazy, Suspense } from "react";

const Component2 = lazy(() => import("./Component2"));
class Component1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isClicked: false
    };
  }
  click () {
    this.setState({ isClicked: true });
  };
  render() {
    return (
      <div>
        <div>component1 </div>
        <button onClick={this.click.bind(this)}>get component2</button>
        {this.state.isClicked && (
          <Suspense fallback={<div>loading...</div>}>
            <Component2 />
          </Suspense>
        )}
      </div>
    );
  }
}

export default Component1;

i tried to write tests for this as follows but could not get a success

import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Component1 from '../src/Component1.js';
Enzyme.configure({ adapter: new Adapter() })
describe('MyComponent', () => {
  it('should render correctly i mode', () => {
    const component = shallow(<Component1 />);
    expect(component).toMatchSnapshot();
  });

  it('should not have component2', () => {
    const component = shallow(<Component1 />);
    expect(component.find('Component2').exists()).toEqual(true);
  })

  it('should contain button getComponent2 and should have component2', ()=> {
    const component = shallow(<Component1 />);
    expect(component.instance().state.isClicked).toEqual(false);
    expect(component.find('button').exists());
    const button = component.find('button');
    button.simulate("click");
    expect(component.instance().state.isClicked).toEqual(true);
    expect(component.find('Component2').exists()).toEqual(true);
    expect(component.find('.component-2').exists()).toEqual(true);
  });  

});
ljharb commented 5 years ago

In general, I'd avoid using simulate.

In your code above, I'm not really clear what's going on - you're not actually using lazy or Suspense.

sahithikol commented 5 years ago

sorry @ljharb i have updated the code added the code snippets where i use react lazy and suspense

shridharkalagi commented 5 years ago

I'm also stuck on a similar issue. Can someone please help?

shridharkalagi commented 5 years ago

@ljharb Can you please help?

sahithikol commented 5 years ago

@ljharb i am stuck on this , can you please suggest

shridharkalagi commented 5 years ago

Any luck on this @sahithikol ?

sahithikol commented 5 years ago

no i could not make any progress on this @shridharkalagi any luck for you @ljharb please help

ljharb commented 5 years ago

@sahithikol Can you try again without using simulate, and using a wrapper.update() after invoking your onClick prop?

sahithikol commented 5 years ago

@ljharb i have tried to use to call component.find('button').props().onClick() instead of simulate , can you please help how can i test this

sahithikol commented 5 years ago

@ljharb i tried testing it this way

import React from 'react';
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Component1 from '../src/Component1.js';
Enzyme.configure({ adapter: new Adapter() })
describe('MyComponent', () => {
  it('should render correctly i mode', () => {
    const component = shallow(<Component1 />);
    expect(component).toMatchSnapshot();
  });

  it('should contain button getComponent2 and should have component2', ()=> {
    const component = shallow(<Component1 />);
    expect(component.instance().state.isClicked).toEqual(false);
    expect(component.find('button').exists());
    component.find('button').prop('onClick')();
    component.update();
    expect(component.instance().state.isClicked).toEqual(true);

    expect(component.find('Component2').exists()).toEqual(true);
    expect(component.find('.component-2').exists()).toEqual(true);
  });

});

but it fails and the error message that i see when i try to debug is ReactDOMServer does not yet support Suspense @shridharkalagi any luck

orpheus commented 4 years ago

In general, I'd avoid using simulate.

@ljharb Why?

ljharb commented 4 years ago

@orpheus it does not actually simulate anything - it's a terrible API name that enzyme inherited from facebook's shallow renderer, before we realized how bad it is. All it does is invoke a prop (with an implicit transformation from the event name to the prop name), and sometimes provides a fake event object. It's much more explicit to directly invoke the prop you need.

ljharb commented 4 years ago

@sahithikol you can't shallow-render nor server-render a component that uses Suspense; instead, that component should avoid using Suspense unless it's already been mounted in a DOM.

johan-smits commented 4 years ago

@ljharb what should be the solution for this? I see many issues about simular issues like: #2254 and #2196

ljharb commented 4 years ago

I believe there is no solution until React provides one, short of avoiding use of Suspense in a server-rendered component.

johan-smits commented 4 years ago

@ljharb thanks for the feedback.

FYI @ramsy-leftclick

sahithikolichala commented 4 years ago

Thanks for all the answers I found out that it works well with the react-testing-library though

ljharb commented 4 years ago

That's because r-t-l uses a full react DOM render; mount should work just fine as well, but you need shallow to be able to unit test and cover server rendering.

AbdelrhmanMagdy commented 3 years ago

Hi @sahithikol , I had a similar issue and it's solved with the help of this package synchronous promise

Just add this snippet to your test file:

import { SynchronousPromise } from 'synchronous-promise';

let __awaiter: Function;

beforeEach(() => {​​​​​
    __awaiter = SynchronousPromise.installGlobally(__awaiter);
}​​​​​);

afterEach(() => {​​​​​
    SynchronousPromise.uninstallGlobally();
}​​​​​);
shinxi commented 2 years ago

FYI the workaround that we used at our project.

  1. Extract Lazy and Suspense to a Loadable component. E.g.,
    
    // Loadable.js
    import React, { Suspense } from 'react';
    import LazyLoadSpinner from '../LazyLoadSpinner';

const Loadable = (config = {}) => { const LazyLoadedComponent = React.lazy(config.loader); return props => ( <Suspense fallback={}> <LazyLoadedComponent {...props} /> ); };

export default Loadable;

2. Use the Loadable component to lazy load your component. E.g., For the case in this issue
```jsx
import React, { lazy, Suspense } from "react";
import Loadable from 'path-to-loadable';

const Component2 = Loadable({
  loader: () => import("./Component2"),
});
class Component1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isClicked: false
    };
  }
  click () {
    this.setState({ isClicked: true });
  };
  render() {
    return (
      <div>
        <div>component1 </div>
        <button onClick={this.click.bind(this)}>get component2</button>
        {this.state.isClicked && (
          <Suspense fallback={<div>loading...</div>}>
            <Component2 />
          </Suspense>
        )}
      </div>
    );
  }
}

export default Component1;
  1. Mock Loadable at your test case, i.e.,
    jest.mock('../Loadable', () => {
    return function({ loader }) {
    loader();
    return function LoadableComponent() {
      return null;
    };
    };
    });
  2. Now you can use 'LoadableComponent' to find the lazy-loaded component. i.e.,
    expect(component.find('LoadableComponent').exists()).toEqual(true);

This is not a complete solution for Suspense&Lazy, but it should be sufficient for unit testing.

Anil-Bhimwal1 commented 2 years ago

what is component in the above line "component.find...."?

ljharb commented 2 years ago

@Anil-Bhimwal1 it's an enzyme wrapper.

Anil-Bhimwal1 commented 2 years ago

@Anil-Bhimwal1 it's an enzyme wrapper.

can anyone help with jest, react-testing-library

ljharb commented 2 years ago

@Anil-Bhimwal1 not on the repo for enzyme, which is neither of those two things.