【问题标题】:React testing component in Jest.js and got TypeError: Cannot read property 'map' of undefined在 Jest.js 中反应测试组件并得到 TypeError: Cannot read property 'map' of undefined
【发布时间】:2025-12-23 16:10:16
【问题描述】:

我有一个由 create-react-app 制作的项目,使用样式化的组件,我正在尝试以许多其他方式测试该组件的一些 DOM 元素,但总是遇到同样的问题。例如在这里我只想记录什么是浅回报。

这是我的 BattleGround.js 组件:

import React, { PureComponent, Fragment } from "react";

import { Title } from "../Common/TitleStyled/TitleStyled";
import { BattleWrapper } from "./BattleWrapperStyled/BattleWrapperStyled";
import { Table } from "./TableStyled/TableStyled";
import { Header } from "../Common/HeaderStyled/HeaderStyled";
import { Result } from "./ResultStyled/ResultStyled";
import { Button } from "../Common/ButtonStyled/ButtonStyled";
import { Loading } from "../Common/LoadingStyled/LoadingStyled";
import { ErrorPage } from "../Common/ErrorPageStyled/ErrorPageStyled";
import CardItem from "../CardItem/CardItem";

class BattleGround extends PureComponent {
  state = {
    atributes: [],
    category: null,
    loading: false
  };

  componentDidMount() {
    this.setAtributes();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.cards !== this.props.cards) {
      this.setState({
        atributes: []
      });
      this.setAtributes();
    }
  }

  renderCard = () => {
    const { cards } = this.props;

    return cards.map((card, key) => {
      return <CardItem card={card} key={key} id={key + 1} />;
    });
  };

  setAtributes = () => {
    const { cards } = this.props;

    cards.forEach(atr => {
      this.setState(state => ({
        atributes: [...state.atributes, atr.mass || atr.crew],
        category: atr.mass ? "people" : "starships"
      }));
    });
  };

  checkWhoWins = () => {
    const { atributes } = this.state;

    if (atributes[0] === "unknown" || atributes[1] === "unknown") {
      return <h2>Atribute unknown. Play again</h2>;
    } else if (parseInt(atributes[0]) > parseInt(atributes[1])) {
      return <h2>Player 1 Wins!</h2>;
    } else if (parseInt(atributes[0]) < parseInt(atributes[1])) {
      return <h2>Player 2 Wins!</h2>;
    } else {
      return <h2>Draw!</h2>;
    }
  };

  playAgain() {
    const { clearCards, fetchCards } = this.props;
    const { category } = this.state;

    this.setState({
      loading: true
    });
    clearCards();
    fetchCards(category);
    fetchCards(category).then(() => this.closeLoading());
  }

  closeLoading() {
    this.setState({
      loading: false
    });
  }

  render() {
    const { errorFetch, resetGame } = this.props;
    const { atributes, loading } = this.state;

    return (
      <Fragment>
        <Header>
          <Title>Moon Wars</Title>
          {errorFetch && <ErrorPage>{errorFetch}</ErrorPage>}
        </Header>
        <BattleWrapper>
          <Table>{this.renderCard()}</Table>
          <Result>{atributes.length === 2 && this.checkWhoWins()}</Result>
          <Button onClick={() => this.playAgain()}>PLAY AGAIN</Button>
          <Button onClick={() => resetGame()}>Go Back</Button>
        </BattleWrapper>
        {loading && <Loading />}
      </Fragment>
    );
  }
}

export default BattleGround;

这是我的 BattleGround.test.js:

import BattleGround from "./BattleGround";

it("renders correctly", () => {
  const wrapper = shallow(
    <BattleGround
      cards={ [{name: "Luke", mass: 10}] }
      clearCards={() => {}}
      fetchCards={() => {}}
      errorFetch={() => {}}
      resetGames={() => {}}
    />
  );

  expect(wrapper).toMatchSnapshot();
});

it("renders correctly detailed", () => {
  const wrapper = render(
    <BattleGround
      cards={[{ name: "Luke", mass: 10 }]}
      clearCards={() => {}}
      fetchCards={() => {}}
      errorFetch={() => {}}
      resetGames={() => {}}
    />
  );

  expect(wrapper).toMatchSnapshot();
});

it('Simple test', () => {
  const wrapper = shallow(<BattleGround />)
  console.log(wrapper)
})

如果需要,这是我的 testSetup.js:

import React from "react";
import Enzyme, { shallow, render, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import { createSerializer } from "enzyme-to-json";
import sinon from "sinon";
import expectExport from "expect";

expectExport.addSnapshotSerializer(createSerializer({ mode: "deep" }));

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

global.React = React;
global.shallow = shallow;
global.render = render;
global.mount = mount;
global.sinon = sinon;

控制台错误:

 FAIL  src/components/BattleGround/BattleGround.test.js
  ✕ Simple test (36ms)
  Renders check
    ✓ renders correctly (9ms)
    ✓ renders correctly detailed (60ms)

  ● Simple test

    TypeError: Cannot read property 'map' of undefined

      34 |     const { cards } = this.props;
      35 | 
    > 36 |     return cards.map((card, key) => {
         |                  ^
      37 |       return <CardItem card={card} key={key} id={key + 1} />;
      38 |     });
      39 |   };

      at BattleGround.map [as renderCard] (src/components/BattleGround/BattleGround.js:36:18)
      at BattleGround.renderCard [as render] (src/components/BattleGround/BattleGround.js:95:24)
      at ReactShallowRenderer._mountClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:845:37)
      at ReactShallowRenderer.render (node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:768:14)
      at render (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:666:53)
      at fn (node_modules/enzyme-adapter-utils/src/Utils.js:99:18)
      at Object.render (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:666:18)
      at new render (node_modules/enzyme/src/ShallowWrapper.js:397:22)
      at shallow (node_modules/enzyme/src/shallow.js:10:10)
      at Object.shallow (src/components/BattleGround/BattleGround.test.js:33:19)

很明显,当测试通过组件时,道具卡是未定义的,但我不知道为什么。这个组件在渲染时应该有来自 App 组件的 props。

【问题讨论】:

    标签: reactjs jestjs enzyme


    【解决方案1】:

    您没有在简单测试中提供道具。 由于您没有定义默认值,因此卡是未定义的。

    it('Simple test', () => {
      const wrapper = shallow(<BattleGround />)
      console.log(wrapper)
    })
    

    【讨论】:

      【解决方案2】:

      您是否尝试过为渲染卡片添加条件,如下所示,这将确保地图功能仅在从道具加载卡片时运行?

      renderCard = () => {
          const { cards } = this.props;
      
          return cards ? cards.map((card, key) => {
            return <CardItem card={card} key={key} id={key + 1} />;
          }: null);
        };
      

      【讨论】:

        最近更新 更多