Как протестировать стилизованные компоненты Material-UI, обернутые с помощью Style, с помощью библиотеки activ-testing? - PullRequest
10 голосов
/ 01 мая 2019

Я пытаюсь создать тест со стилизованным компонентом Material-UI, используя библиотеку реагировать на тестирование в машинописи. Мне трудно получить доступ к внутренним функциям компонента, чтобы высмеивать и утверждать.

Form.tsx

export const styles = ({ palette, spacing }: Theme) => createStyles({
    root: {
        flexGrow: 1,
    },
    paper: {
        padding: spacing.unit * 2,
        margin: spacing.unit * 2,
        textAlign: 'center',
        color: palette.text.secondary,
    },
    button: {
        margin: spacing.unit * 2,
    }
});

interface Props extends WithStyles<typeof styles> { };

export class ExampleForm extends Component<Props, State> {
  async handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    // Handle form Submit
    ...
    if (errors) {
            window.alert('Some Error occurred');
            return;
        }
  }
  // render the form
}
export default withStyles(styles)(ExampleForm);

Test.tsx

import FormWithStyles from './Form';

it('alerts on submit click', async () => {
  jest.spyOn(window,'alert').mockImplementation(()=>{});
  const spy = jest.spyOn(ActivityCreateStyles,'handleSubmit');
  const { getByText, getByTestId } = render(<FormWithStyles />)
  fireEvent.click(getByText('Submit'));

  expect(spy).toHaveBeenCalledTimes(1);
  expect(window.alert).toHaveBeenCalledTimes(1);
})

jest.spyOn выдает следующую ошибку Argument of type '"handleSubmit"' is not assignable to parameter of type 'never'.ts(2345), вероятно, потому что ExampleForm обернут в withStyles.

Я также пытался напрямую импортировать компонент ExampleForm и назначать стили вручную, но сделать это было невозможно:

import {ExampleForm, styles} from './Form';

it('alerts on submit click', async () => {
  ...

  const { getByText, getByTestId } = render(<ActivityCreateForm classes={styles({palette,spacing})} />)

  ...
}

Получена следующая ошибка: Type '{ palette: any; spacing: any; }' is missing the following properties from type 'Theme': shape, breakpoints, direction, mixins, and 4 more.ts(2345)

Мне трудно писать базовые тесты в Typescript для Material-UI компонентов с react-testing-library & Jest из-за строгой типизации и переносимых компонентов. Пожалуйста, руководство.

Ответы [ 3 ]

3 голосов
/ 02 июля 2019

Прежде всего, когда вы используете render метод act-testing-library , вам не нужно беспокоиться об использовании withStyles или какой-либо оболочки, потому что в конце он отображает компонент как может быть в реальном дом, так что вы можете написать свои тесты в обычном режиме.

Тогда, насколько я понимаю, вы делаете то же самое, что я делал, когда начинал с тестов (это значит, что вы станете хорошими в этом;). Вы пытаетесь смоделировать внутренний метод, и это не лучший подход, которому нужно следовать, потому что вам нужно протестировать реальный метод.

Итак, давайте представим, что у нас есть Register пользовательский компонент.

ЦСИ / Register.tsx

import ... more cool things
import * as api from './api';

const Register = () => {
  const [name, setName] = useState('');
  const handleNameChange = (event) => {
    setName(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    if (name) {
      api.registerUser({ name });
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <TextField
        id='name'
        name='name'
        label='Name'
        fullWidth
        value={name}
        onChange={handleNameChange}
      />
      <Button data-testid='button' fullWidth type='submit' variant='contained'>
        Save
      </Button>
    </form>
  );
}

Компонент довольно прост, это форма с вводом и кнопкой. Мы используем react hooks для изменения входного значения и на основании того, что мы вызываем или нет api.registerUser, когда происходит событие handleSubmit.

Для тестирования компонента первое, что нам нужно сделать, - это метод mock api.registerUser.

SRC / __ тесты __ / Register.tsx

import * as api from '../api'

jest.mock('../api')

api.registerUser = jest.fn()

Это позволит нам увидеть, вызывается ли этот метод или нет.

Следующее, что нужно сделать, это ... написать тесты, в этом сценарии мы можем протестировать две вещи, чтобы убедиться, что handleSubmit работает правильно.

  1. Не звонить api.registerUser, если имя пусто .
it('should not call api registerUser method', () => {
  const { getByTestId } = render(<Register />)
  fireEvent.click(getByTestId('button'))
  expect(api.registerUser).toHaveBeenCalledTimes(0)
})
  1. Позвоните api.registerUser, если имя не пусто.
it('should call api registerUser method', () => {
  const { getByLabelText, getByTestId } = render(<Register />)

  fireEvent.change(getByLabelText('Name'), { target: { value: 'Steve Jobs' }})

  fireEvent.click(getByTestId('button'))
  expect(api.registerUser).toHaveBeenCalledTimes(1)
})

В этом последнем тесте мы также косвенно тестируем handleNameChange, потому что мы меняем имя :), поэтому name не будет пустым и будет вызываться registerUser.

Пример с withStyles и машинопись находится в этом репо .
Демо-версия здесь .

2 голосов
/ 02 июля 2019

Вы можете использовать unwrap, чтобы развернуть упакованный стилизованный компонент, а затем протестировать его

import { unwrap } from '@material-ui/core/test-utils';
import {ExampleForm, styles} from './Form';

it('alerts on submit click', async () => {
  ...
const unwrapped = unwrap(ExampleForm);
    ...
}

Тогда вы можете сделать необходимое тестирование на развернутом объекте

2 голосов
/ 26 июня 2019

Почему бы вам не использовать enzyme с Full DOM Rendering ? Вы можете использовать метод simulate для имитации событий на смонтированных компонентах.

class Foo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <div className={`clicks-${count}`}>
          {count} clicks
        </div>
        <a href="url" onClick={() => { this.setState({ count: count + 1 }); }}>
          Increment
        </a>
      </div>
    );
  }
}

const wrapper = mount(<Foo />);

expect(wrapper.find('.clicks-0').length).to.equal(1);
wrapper.find('a').simulate('click');
expect(wrapper.find('.clicks-1').length).to.equal(1);
...