Как мне поверхностно протестировать компонент React, заключенный в memo и withStyles?

У меня есть компонент, который заключен как в Material-UI withStyles HOC, так и в React memo HOC.

Я не могу протестировать этот компонент, так как не могу вызвать dive():

ShallowWrapper::dive() can only be called on components

Единственный вариант, о котором я сейчас знаю, - это независимо export Demo и export default withStyles(styles)(Demo). Это позволяет мне протестировать компонент, не заключенный в withStyles. Я бы хотел избежать этого метода.

Если я удалю memo (), я смогу протестировать компонент. Точно так же, если я удалю withStyles (), я также смогу протестировать компонент. Комбинация этих HOC делает мой компонент не тестируемым.

Какие существуют стратегии для эффективного тестирования этого компонента?

demo.js

import React, { memo } from "react";
import MUIIconButton from "@material-ui/core/IconButton";
import { withStyles } from "@material-ui/core/styles";
import Tooltip from "@material-ui/core/Tooltip";
import Typography from "@material-ui/core/Typography";

const styles = () => ({
  root: {
    backgroundColor: "red"
    /* more styles... */
  }
});

const Demo = memo(({ label, classes }) => (
  <div className={classes.root}>
    <Tooltip disableFocusListener title={label}>
      <Typography>label</Typography>
    </Tooltip>
  </div>
));

export default withStyles(styles)(Demo);

demo.test.js

import React from "react";
import Adapter from "enzyme-adapter-react-16";
import { configure, shallow } from "enzyme";
import Demo from "./demo";
import MUIIconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

configure({ adapter: new Adapter() });

describe("Demo", () => {
  it("Should have a tooltip with label", () => {
    const tooltip = "My tooltip";

    const el = shallow(<Demo label={tooltip} />).dive();

    expect(el.find(Tooltip).props().title).toEqual(tooltip);
  });
});

Полноценная рабочая песочница

  Изменить 2j3o14zxy0


person Ed Allonby    schedule 05.03.2019    source источник
comment
проверьте github.com/mui-org/material-ui/issues/9266 - есть обходной путь с createShallow. Но я бы предпочел использовать отдельный экспорт, как вы это уже делаете. Это более надежный способ. В некоторых случаях, таких как компоненты, подключенные к Redux, это официальное предложение. Так не лучше ли использовать один и тот же подход для разных компонентов?   -  person skyboyer    schedule 05.03.2019


Ответы (3)


Как предлагает Skyboyer, вам следует просто export памятную функцию. Вы можете import экспорт по умолчанию HOC и использовать mount, но вам нужно смоделировать объект classes, чтобы он соответствовал тому, как он используется в компоненте.

Рабочий пример: https://codesandbox.io/s/4r492qvoz9

компоненты / Demo / demo.js

import React, { memo } from "react";
import MUIIconButton from "@material-ui/core/IconButton";
import { withStyles } from "@material-ui/core/styles";
import Tooltip from "@material-ui/core/Tooltip";
import Typography from "@material-ui/core/Typography";

const styles = () => ({
  root: {
    backgroundColor: "red"
    /* more styles... */
  }
});

export const Demo = memo(({ label, classes }) => {
  return (
    <div className={classes.root}>
      <Tooltip disableFocusListener title={label}>
        <Typography>label</Typography>
      </Tooltip>
    </div>
  );
});

export default withStyles(styles)(Demo);

components / Demo / __ tests __ / demo.test.js, если когда-нибудь понадобится увидеть структуру DOM, просто используйте console.log(wrapper.debug()); - например console.log(mountHOComponent.debug());)

import React from "react";
import Adapter from "enzyme-adapter-react-16";
import { configure, shallow, mount } from "enzyme";
import { Demo } from "../demo";
import HOCDemo from "../demo";

configure({ adapter: new Adapter() });

const initialProps = {
  label: "My tooltip",
  classes: {
    root: "component-example"
  }
};

const shallowWrapper = shallow(<Demo {...initialProps} />);
const mountWrapper = mount(<Demo {...initialProps} />);
const mountHOComponent = mount(<HOCDemo {...initialProps} />);

describe("Demo", () => {
  afterAll(() => {
    shallowWrapper.unmount();
    mountWrapper.unmount();
  });

  it("shallowWrap renders a tooltip with label", () => {
    expect(shallowWrapper.find("WithStyles(Tooltip)").props().title).toBe(
      initialProps.label
    );
  });

  it("mountWrap renders a tooltip with label", () => {
    expect(mountWrapper.find("Tooltip").props().title).toBe(initialProps.label);
  });

  it("mountHOComponent renders a tooltip with label", () => {
    expect(mountHOComponent.find("Tooltip").props().title).toBe(
      initialProps.label
    );
  });
});
person Matt Carlotta    schedule 05.03.2019
comment
Спасибо, мы решили перекусить и использовать этот подход, и он работает хорошо. Но я до сих пор не знаю, почему shallow (el) .dive (). Dive () (одно погружение на правило HOC) не сработало так, как ожидалось. - person Ed Allonby; 07.03.2019

Когда я оборачиваю памятку, я получаю такую ​​форму

import MemoizedFoo from './Foo'
console.log(MemoizedFoo) 

    { '$$typeof': Symbol(react.memo),
      type:
       { [Function: Foo]
         displayName: 'Foo',
         defaultProps: { theme: {} } },
      compare: null }

поэтому в моем шутливом тесте я могу получить внутренний компонент, указав ключ типа

import MemoizedFoo from './Foo'
const Foo = MemoizedFoo.type

describe() { it() { shallow(Foo) ...etc } }

Это отлично подходит для неглубоких модульных тестов.

Если бы я монтировал родительский компонент и искал присутствующих детей, вы могли бы сделать что-то вроде этого:

wrapper = mount(Layout)
wrapper.find('Memo(Foo)')
person lfender6445    schedule 15.08.2019

Теперь это исправлено в фермент-адаптер-реагировать-16 v1.13.0, который добавлена ​​поддержка memo dive (). Вот разветвленная песочница с обновленной зависимостью, чтобы показать, что оба метода тестирования (обходной путь погружения и экспорта) теперь проходят.

 Редактировать тестовую записку и со стилями

person Ed Allonby    schedule 05.06.2019