Pull to refresh

Comments 13

Возможно вам стоит подойти с другой стороны к тестам: писать классические юниты, все что за скоупом — мокать, без этого это уже не юниты а интеграционные тесты, а интеграционные отдать на селениум, джин и тд
Так я в сущности так и делаю тут. Я задумал тестировать публичные «действия юзера» — тот код контроллера, который они дергают, еще не означает интеграционный тест; и даже механизмы react при этом не задействуются: только код обработчика, другие какие-то методы компонента, и переданный props.setXXX; ну и возможно заодно какие-то сервисы (которые в этом случае следует так или иначе замокать). Вроде бы вполне модульно получается?
Ну, то есть я отдельно в тексте не выделил, что внешние зависимости следует мокать, но это как бы в контексте юнит-тестирования само собой подразумевается. Добавлю указание, что речь именно о юнит-тестировании.
Второй способ плох [...] еще тем, что это не вполне корректно (реально действия пользователя часто вызывают сразу несколько событий [...])

Что вы имеете здесь в виду? Например, есть кнопка


<Button onClick={this.handleButtonClick} />

на нее можно кликнуть в тесте


component.find('Button').simulate('click')

На мой взгляд, все корректно. Есть интерактивный элемент, мы сымитировали клик по нему. Какие дополнительные события мог вызвать реальный пользователь, а мы их не протестировали?

Сперва hover, а вдруг у вас кнопка, когда на нее наводится — не нажимается? Или, допустим, сама кнопка вроде нажимается, но де-факто поверх нее вылазит другой блок, который ее прикрывает и не дает нажать?

Но в подавляющем большинстве случаев мы имеем простую кнопку, которая легко тестируется через .simulate('click')
А для экзотических вариантов можно поискать и другой вариант тестирования

1) если у вас очень простые внутренности у компонента — как в вашем примере один button — то может и не стоит городить огород из клика по нему (я ж там чуть дальше и пишу что возможно и не стоит заморачиваться). А если у вас две кнопки, то выбор одной из них начинает выглядеть уже некрасиво, разве что у них еще и id например есть; хотя и тут вы можете сами для себя решить, что проще их искать каким-то селектором, чем дергать через апи.
2) TheShock привел пример. Понятно что этот hover может быть и не обрабатывается в приложении, но факт тот, что мы с одной стороны как бы «имитируем событие», а с другой, в реальной жизни оно в таком виде и одно — не возникает. А вызов метода «пользователь нажал на кнпоку» — это уже чуть выше по уровню абстракции и можно там игнорировать какую-то специфику работы браузера (ненужные нам события).

Зато если тестировать через метод, не нажимая на кнопку, то может так получиться, что кнопки в UI не видно, пользователь на нее нажать не может, а тест успешно проходит. Например, если на кнопке есть атрибут disabled или просто условие, по которому она не показывается.


Не вижу смысла параноить по поводу того, что simulate это не совсем честный клик. Другие события, кроме клика, используются очень редко, а вот условный показ контрола — почти в каждой форме.

Ну это нормально — отдельный тест проверяет что кнопку в некой ситуации нельзя нажать. Одно условие — один тест. Вот тут я не знаю, как лучше это сделать — простейший способ при помощи сравнения с эталонной разметкой (допустим, проверяется что на кнопке есть класс button-disabled), но ведь она может быть и неправильной. Если тут именно simulate сработает более правильно — учитывая все атрибуты — то возможно все же следует его использовать.
На самом деле проверка того что кнопку нажать не получается — это интеграционный тест. Он проверяет 1) классы 2) получившиеся в результате атрибуты 3) какие-то контролы которые возможно кнопку закрывают на экране (те же модалки всякие), и тд; может, для этого даже лучше прикручивать всякие разные селениумы, которые для интеграционного и предназначены?

Да, disabled атрибут учитывается, если использовать mount, и игнорируется если пользоваться shallow:
https://github.com/airbnb/enzyme/issues/386


Тестируйте такие кейсы в mount и будет все нормально.

Однако, вернемся к вопросу, как лучше тестировать: через (как бы)публичный метод или взаимодействие с разметкой.


Из моего опыта кажется, что через разметку будет лучше. В React-компонентах верстка расположена вместе с js-кодом, поэтому компонент очень удобно рассматривать как черный ящик. Мы рендерим его с некоторыми пропсами, потом взаимодействуем через отрендеренный UI. Для поиска элемента приходит на помощь паттерн page object, вот его пример его использования в контексте React.


В подходе же с публичными методами придется специально держать методы доступными только для тестирования (что уже подозрительно), кроме того, придется написать дополнительные тесты, чтобы проверить еще и отрендеренный UI. В итоге это получается whitebox-тестирование, которые тоже иногда допустимо, но его сложнее смаппить на реальные пользовательские сценарии, чем blackbox.

Понимаю ваши рассуждения, но я как раз и попытался взглянуть на ситуацию с той стороны, которая позволяет рассматривать это по-прежнему как blackbox. Сделать decoupling между разметкой и контроллером — и тестировать не компонент в целом как блекбокс, а два куска компонента как два блекбокса. Возможно, я перемудрил с этим, но до тестирования реализации мы тут не скатываемся. Да и почему сложнее ложится на реальные сценарии? Мы просто вместо «поставил стейт и нажал кнопку» будем делать «поставил стейт и сказал что нажал кнопку» — практически одно к одному.
Если ваш опыт говорит, что без проблем можно действовать через симуляцию enzyme и component.find — возможно, так и следует делать, а не как я предложил.

1) было бы логично искать/проверять кнопки по их тексту/названию (как их использует обычный пользователь).
Например, с помощью :


const buttons = scryRenderedDOMComponentsWithTag(component, `button`);
expect(buttons.length).to.equal(2);
expect(buttons[0].textContent).to.equal(`Да`);
expect(buttons[1].textContent).to.equal(`Нет`);
Only those users with full accounts are able to leave comments. Log in, please.