React 单元测试 -> 模拟点击 child 调用方法

React unit testing -> simulate click on child to invoke method

假设我在 React 中实现了一个简单的 pexeso 游戏 (for those who do not know pexeso - this is a random pexeso example - not mine)。

目前重要的是,游戏由一个 GameBoard 组件组成,其中显示大量 Card 组件。

GameBoard 组件的状态是一组当前翻转的纸牌 - this.state.flipped_cards.

单击卡片后,它会调用内部函数(强制翻转卡片)。在这个函数中,涉及到GameBoard的handleCardFlip函数来判断最终的游戏状态。

从 GameBoard 返回的结果 JSX 看起来像这样:

<div>
  <Card onClick={this.handleFlippedCard} name="MistyRose" />
  <Card onClick={this.handleFlippedCard} name="Olive" />
  <Card onClick={this.handleFlippedCard} name="MistyRose" />
  <Card onClick={this.handleFlippedCard} name="Olive" />
</div>

handleFlippedCard( flipped_card_text ) {
    if ( this.state.flipped_cards.length < MAX_FLIPPED_CARDS ) {
        this.setState(
            {
                flipped_cards:
                this.state.flipped_cards.concat( [flipped_card_text] )
            },
            () => {
                __resolveGameState.bind( this )()
            }
        )
    }
} // handleFlippedCard

现在我想为我的代码编写单元测试。目前我正在使用 jest、enzyme 和 chai(我有点不知所措,老实说,我真的对所有疯狂的测试引擎名称感到困惑 :)))。

如何模拟对卡片的点击?我阅读了一些有关间谍和存根的内容,但并没有真正理解 how/when 来使用它们。

it.only( 'Removes flipped cards if they match', () => {
        const board = shallow( <GameBoard cardsInput={ cardsInput } /> )
        // reveal first card
        board.setState( { 
            flipped_cards: 
                board.state( 'flipped_cards' ).concat( cardsInput[1] )
        } )

        // reveal second card
        board.setState( {
           flipped_cards: 
               board.state( 'flipped_cards' ).concat( cardsInput[3] )
        } )

        // thought the handleFlipped cards would get invoked here..

        expect( board.state( 'visible_cards' ).length ).to
                .equal( cardsInput.length-2 )
    } )

我会欢迎任何评论,我是新手。

基于你的 jsx

<div>
  <Card onClick={this.handleFlippedCard} name="MistyRose" />
  <Card onClick={this.handleFlippedCard} name="Olive" />
  <Card onClick={this.handleFlippedCard} name="MistyRose" />
  <Card onClick={this.handleFlippedCard} name="Olive" />
</div>

您可以通过

在每个<Card>组件上模拟点击事件
const wrapper = shallow(
  // Pass the required props to GameBoard component if any
  <GameBoard />
);

wrapper.find('Card').forEach(function (card) {
    let cardName = card.prop('name');
    card.simulate('click', {
        preventDefault: () => {
        },
        target: [
          {
            value: cardName,
          }
        ]
      });
});

如果你想 select 每个组件分别通过道具,你也可以通过 -

let card = wrapper.find('[name="Olive"]').first();

然后在返回的卡片组件shallow wrapper上类似地模拟点击事件。

另请记住,模拟点击事件将调用 handleFlippedCard,但它会将关联的事件对象传递给您的处理程序,您必须从接收到的事件对象中获取所需的值。