Макет импортированного компонента Lazy React

Вот мой ленивый компонент:

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

Импортирую вот так:

import Bones from './LazyBones'

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

И в моем тесте у меня есть такие вещи:

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

})

Я ожидаю, что тест пройдет, а журнал console.log распечатает:

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

Но вместо <Bones /> я получил <lazy /> и он не проходит тест.

Как я могу имитировать импортированный компонент Lazy React, чтобы мой упрощенный тест прошел?

4 ответа

Решение

Я не уверен, что это именно тот ответ, который вы ищете, но похоже, что часть проблемы shallow. Согласно этой теме,shallow не будет работать с React.lazy.

Однако, mount также не работает при попытке заглушить ленивый компонент - если вы отлаживаете вывод DOM (с console.log(wrapper.debug())) ты это видишь Bones находится в DOM, но это настоящая (не заглушенная) версия.

Хорошие новости: если вы только пытаетесь это проверить Bonesсуществует, вам вообще не нужно имитировать компонент! Этот тест проходит:

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

Если вам нужно издеваться над компонентом по другой причине, jest позволит вам это сделать, но похоже, что вы пытаетесь избежать jest. В этой ветке обсуждаются некоторые другие варианты в контекстеjest(например, издевательствоSuspense а также lazy), который также может работать с sinon.

Вам не нужно разрешать lazy() функция с помощью .then(x => x.default) React уже делает это за вас.

React.lazy принимает функцию, которая должна вызывать динамический импорт (). Это должно вернуть Promise, который разрешается в модуль с экспортом по умолчанию, содержащим компонент React. Реагировать на разделение кода

Синтаксис должен выглядеть примерно так:

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

Пример:

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

Редактировать response-lazy-component-test


Что касается тестирования, вы можете следовать примеру тестирования React, который по умолчанию поставляется вcreate-react-app и немного измените это.

Создайте новый файл с именем LazyComponent.test.js и добавить:

// 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()
})

Живой пример: щелкните вкладку " Тесты" рядом с вкладкой " Браузер ". если не работает, просто перезагрузите страницу.

Редактировать response-lazy-component-testing

Вы можете найти больше сложных примеров реакции-тестирования-библиотеки на их сайте Документов.

Мне нужно было протестировать мой ленивый компонент с помощью Enzyme. Следующий подход помог мне проверить завершение загрузки компонента:

      const myComponent = React.lazy(() => 
      import('@material-ui/icons')
      .then(module => ({ 
         default: module.KeyboardArrowRight 
      })
   )
);

Тестовый код ->

      //mock actual component inside suspense
jest.mock("@material-ui/icons", () => { 
    return {
        KeyboardArrowRight: () => "KeyboardArrowRight",
}
});

const lazyComponent = mount(<Suspense fallback={<div>Loading...</div>}>
           {<myComponent>}
       </Suspense>);
    
const componentToTestLoaded  = await componentToTest.type._result; // to get actual component in suspense
    
expect(componentToTestLoaded.text())`.toEqual("KeyboardArrowRight");

Это хакерство, но хорошо работает для библиотеки Enzyme.

Чтобы издеваться над вашим ленивым компонентом, сначала подумайте, это преобразовать тест в асинхронный и подождать, пока компонент не появится, например:

import CustomComponent, { Bones } from './Components';

it('renders bones', async () => {
   const wrapper = mount(<Suspense fallback={<p>Loading...</p>}>
                       <CustomComponent />
                   </Suspense>

   await Bones;
   expect(wrapper.exists(Bones)).toBeTruthy();
}
Другие вопросы по тегам