Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock out imported Lazy React component

Here's my lazy component:

const LazyBones = React.lazy(() => import('@graveyard/Bones')
  .then(module => ({default: module.BonesComponent}))
export default LazyBones

I'm importing it like this:

import Bones from './LazyBones'

export default () => (
<Suspense fallback={<p>Loading bones</p>}>
  <Bones />
</Suspense>
)

And in my test I have this kind of thing:

import * as LazyBones from './LazyBones';

describe('<BoneYard />', function() {
  let Bones;
  let wrapper;
  beforeEach(function() {
    Bones = sinon.stub(LazyBones, 'default');
    Bones.returns(() => (<div />));
    wrapper = shallow(<BoneYard />);
  });
  afterEach(function() {
    Bones.restore();
  });

  it('renders bones', function() {
    console.log(wrapper)
    expect(wrapper.exists(Bones)).to.equal(true);
  })

})

What I expect is for the test to pass, and the console.log to print out:

<Suspense fallback={{...}}>
  <Bones />
</Suspense>

But instead of <Bones /> I get <lazy /> and it fails the test.

How can I mock out the imported Lazy React component, so that my simplistic test passes?

like image 512
AncientSwordRage Avatar asked Sep 11 '19 13:09

AncientSwordRage


People also ask

How do you test the React lazy component?

You can use @testing-library/react to test the React. lazy() method. In addition, if the lazy-loaded component is too complex and you only want to test the parent component that contains the component, you can use the jest. mock() method To provide a mocked version of the subcomponent.

How do you test if a component is rendered with the right props when using React testing library?

Testing The ParentComponent is Setting Props Correctly render(<ParentComponent open data="some data" />); // Check that the Jest mock function is called with an object. // Use 'expect. objectContaining' to make sure any other default // React props are ignored. expect(mockChildComponent). toHaveBeenCalledWith( expect.

How do I use dynamic import in React?

import { lazy, Suspense } from "react"; import { Routes, Route, Outlet, Link } from "react-router-dom"; import HomePage from "./pages/Home"; const Dashboard = lazy(() => import("./pages/Dashboard")); const Notifications = lazy(() => import("./pages/Notifications")); export default function App() { return ( <div ...

Does React lazy increase performance?

Benefits of lazy loading React componentThe major benefit of React lazy load is performance. Loading less JavaScript code to the browser will reduce DOM load time and boost the performance of our application. Users are able to access a web page even if everything has not been loaded.


2 Answers

I'm not sure this is the answer you're looking for, but it sounds like part of the problem is shallow. According to this thread, shallow won't work with React.lazy.

However, mount also doesn't work when trying to stub a lazy component - if you debug the DOM output (with console.log(wrapper.debug())) you can see that Bones is in the DOM, but it's the real (non-stubbed-out) version.

The good news: if you're only trying to check that Bones exists, you don't have to mock out the component at all! This test passes:

import { Bones } from "./Bones";
import BoneYard from "./app";

describe("<BoneYard />", function() {
  it("renders bones", function() {
    const wrapper = mount(<BoneYard />);
    console.log(wrapper.debug());
    expect(wrapper.exists(Bones)).to.equal(true);
    wrapper.unmount();
  });
});

If you do need to mock the component for a different reason, jest will let you do that, but it sounds like you're trying to avoid jest. This thread discusses some other options in the context of jest (e.g. mocking Suspense and lazy) which may also work with sinon.

like image 157
helloitsjoe Avatar answered Sep 28 '22 17:09

helloitsjoe


You don't need to resolve lazy() function by using .then(x => x.default) React already does that for you.

React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. React code splitting

Syntax should look something like:

const LazyBones = React.lazy(() => import("./LazyBones"))

Example:

// LazyComponent.js
import React from 'react'

export default () => (
  <div>
    <h1>I'm Lazy</h1>
    <p>This component is Lazy</p>
  </div>
)

// App.js
import React, { lazy, Suspense } from 'react'
// This will import && resolve LazyComponent.js that located in same path
const LazyComponent = lazy(() => import('./LazyComponent'))

// The lazy component should be rendered inside a Suspense component
function App() {
  return (
    <div className="App">
      <Suspense fallback={<p>Loading...</p>}>
        <LazyComponent />
      </Suspense>
    </div>
  )
}

Edit react-lazy-component-test


As for Testing, you can follow the React testing example that shipped by default within create-react-app and change it a little bit.

Create a new file called LazyComponent.test.js and add:

// LazyComponent.test.js
import React, { lazy, Suspense } from 'react'
import { render, screen } from '@testing-library/react'

const LazyComponent = lazy(() => import('./LazyComponent'))

test('renders lazy component', async () => {
  // Will render the lazy component
  render(
    <Suspense fallback={<p>Loading...</p>}>
      <LazyComponent />
    </Suspense>
  )
  // Match text inside it
  const textToMatch = await screen.findByText(/I'm Lazy/i)
  expect(textToMatch).toBeInTheDocument()
})

Live Example: Click on the Tests Tab just next to Browser tab. if it doesn't work, just reload the page.

Edit react-lazy-component-testing

You can find more react-testing-library complex examples at their Docs website.

like image 40
awran5 Avatar answered Sep 28 '22 16:09

awran5