Модульное тестирование компонента React с использованием диалогового окна пользовательского интерфейса материала

В настоящее время я пишу модульные тесты для своего приложения React + MaterialUi.

В моем приложении есть диалог. Я хочу убедиться, что в зависимости от того, какая кнопка нажата в диалоговом окне:

<FlatButton
  label="Cancel"
  secondary={true}
  onTouchTap={this._cancelDialog.bind(this)} 
 />
 <FlatButton
   label="Submit"
   primary={true}
   onTouchTap={this._confirmDialog.bind(this)} 
 />

что внутреннее состояние изменится соответственно.

К сожалению, я не могу получить содержимое диалогового окна с помощью TestUtils.scryRenderedComponentsWithType(FlatButton)
или
scryRenderedComponentsWithTag("button")
и так далее.

Есть идеи, как можно проверить этот поток?

Обновление 1

Итак, я могу получить экземпляр Dialog, вызвав TestUtils.scryRenderedComponentsWithType (Dialog). Но я не могу получить содержимое диалогов. Что касается DOM, контент не отображается внутри самого представления. Он отображается в новом созданном узле на уровне документа (div). Итак, я попробовал это:

let cancelButton = window.document.getElementsByTagName("button")[0];
Simulate.click(cancelButton);

cancelButton в приведенном выше случае является правильным элементом DOM. Однако Simulate.click не запускает функцию щелчка по компонентам.

С уважением, Йонас


person jonas.hartwig    schedule 16.12.2015    source источник
comment
не могли бы вы изменить свой вопрос в более удобочитаемом формате?   -  person Naisheel Verdhan    schedule 16.12.2015


Ответы (5)


просто столкнулся с той же проблемой. Я просмотрел исходный код, и метод рендеринга компонента Dialog фактически создает экземпляр компонента RenderToLayer. этот компонент ведет себя как портал и разбивает дерево DOM реакции, возвращая ноль в своей функции рендеринга и вместо этого добавляя непосредственно к телу.

К счастью, компонент RenderToLayer принимает опору render, которая, по сути, позволяет компоненту передать порталу функцию, которая будет вызываться, когда он находится в цикле рендеринга. Это означает, что мы можем сами вручную инициировать это событие. Я признаю, что это не идеально, но после нескольких дней копаний, пытающихся найти решение для этого взлома, я бросаю это полотенце и пишу свои тесты следующим образом:

var component = TestUtils.renderIntoDocument(<UserInteractions.signupDialog show={true}/>)
var dialog = TestUtils.renderIntoDocument(component.refs.dialog.renderLayer())
var node = React.findDOMNode(dialog)

и вот как выглядит мой UserInteractions.signupDialog:

exports.signupDialog = React.createClass({
...
  render: function() {
    var self = this;

    return (
      <div>
        <Dialog
          ref='dialog'
          title="Signup"
          modal={false}
          actions={[
            <Button
              label="Cancel"
              secondary={true}
              onTouchTap={self.__handleClose}
            />,
            <Button
              label="Submit"
              primary={true}
              keyboardFocused={true}
              onTouchTap={self.__handleClose}
            />
          ]}
          open={self.props.show}
          onRequestClose={self.__handleClose}
          >
          <div className='tester'>ham</div>
          <TextField id='tmp-email-input' hintText='email' type='text'/>
        </Dialog>
      </div>
    )
  }
})

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

Я определенно рекомендую настроить отладчик в вашем тестовом стеке, если вы собираетесь и дальше использовать Material ui. В таких вещах не так много помощи. Вот как выглядит мой сценарий отладки:

// package.json
{
  ...
  "scripts": {
    "test": "mocha --compilers .:./test/utils/compiler.js test/**/*.spec.js",
    "debug": "mocha debug --compilers .:./test/utils/compiler.js test/**/*.spec.js"
  }
}

и теперь вы можете использовать npm test для запуска тестов мокко и npm run debug для входа в отладчик. Оказавшись в отладчике, он немедленно остановится и будет ждать, пока вы введете точки останова. На этом этапе введите c, чтобы продолжить. Теперь вы можете размещать операторы debugger; в любом месте вашего кода, чтобы сгенерировать точку останова, на которую будет реагировать отладчик. Как только он обнаружит вашу точку останова, он остановится и позволит вам задействовать ваш код, используя локальную область видимости. На этом этапе введите repl, чтобы войти в локальную область действия вашего кода и получить доступ к вашим локальным переменным.

Возможно, вам не понадобился отладчик, но, возможно, кому-то это пригодится. Удачи, удачного кодирования!

person avocadojesus    schedule 01.02.2016
comment
Я долго курил голову, чтобы решить эту проблему с помощью тестирования диалога. Отличный ответ. Спасибо за это. - person codeVerine; 08.05.2016

Решил это следующим образом:

/*
* I want to verify that when i click on cancel button my showModal state is set * to false
*/

//shallow render my component having Dialog
const wrapper= shallow(<MyComponent store={store} />).dive();

//Set showModal state to true
wrapper.setState({showModal:true});

//find out cancel button with id 'cancelBtn' object from actions and call onTouchTap to mimic button click
wrapper.find('Dialog').props().actions.find((elem)=>(elem.props.id=='cancelBtn')).props.onTouchTap();

//verify that the showModal state is set to false
expect(wrapper.state('showModal')).toBe(false);
person gsingh    schedule 09.03.2017

Я столкнулся с той же проблемой и решил ее так:

const myMock = jest.genMockFunction();
const matcherComponent = TestUtils.renderIntoDocument(
      <MatcherComponent onClickCancel={myMock} activAction/>
        );
const raisedButton = TestUtils.findRenderedComponentWithType(
            matcherComponent, RaisedButton);

TestUtils.Simulate.click(ReactDOM.findDOMNode(raisedButton).firstChild);

expect(myMock).toBeCalled();

Он отлично работает для меня. Однако я все еще борюсь с Simulate.change

person Martin Bieth    schedule 18.12.2015
comment
Спасибо за ответ. К сожалению, TestUtils.findRenderedComponentWithType (dialog, FlatButton) ничего не возвращает в случае диалога. - person jonas.hartwig; 28.12.2015
comment
Я сам столкнулся с той же проблемой с Dialog .. Может быть, проблема с диалогом Material UI? - person Martin Bieth; 30.12.2015

Решение от avocadojesus отличное. Но у меня есть одно дополнение. Если вы попытаетесь применить это решение и получите сообщение об ошибке:

ОШИБКА: 'Предупреждение: сбойный тип контекста: контекст muiTheme помечен как требуемый в DialogInline, но его значение равно undefined.

Вы должны изменить его код следующим образом:

var component = TestUtils.renderIntoDocument(
  <MuiThemeProvider muiTheme={getMuiTheme()}>
    <UserInteractions.signupDialog show={true}/>
  </MuiThemeProvider>
);

var dialogComponent = TestUtils.findRenderedComponentWithType(component, UserInteractions.signupDialog);

var dialog = TestUtils.renderIntoDocument(
  <MuiThemeProvider muiTheme={getMuiTheme()}>
    {dialogComponent.refs.dialog.renderLayer()}
  </MuiThemeProvider>
);

var node = React.findDOMNode(dialog);
person Mikhail Shabrikov    schedule 21.07.2017

Материальный интерфейс UI включает 2 ферментных метода. Вам необходимо использовать createMount или createShallow с опцией погружения https://material-ui.com/guides/testing/#createmount-options-mount

person Laurent    schedule 16.04.2020