如何在React测试中模拟状态和数据的值?

7
我正在为我的React页面编写测试,但是我的页面在其状态中使用isLoading,当加载页面时呈现'Loading',当加载完成但没有数据(来自获取请求)时呈现'未找到数据',当加载有数据(来自获取请求)时,它会加载欢迎页面。
我想编写一个测试,检查当以下情况时是否显示了预期的文本:
- 加载页面但未发现任何数据 - 加载页面并具有数据
我认为我已经成功地让没有数据的测试正确运行,但是当我尝试模拟返回的数据时,我遇到了错误消息。错误信息是:
TypeError: Cannot read property 'map' of undefined

但我不确定为什么我的模拟数据是data:orders>,其中有数据,因此不应该是未定义的。

可以有人告诉我如何更正测试吗?

WelcomePage.js

import React from "react";
import { Link } from "react-router-dom";

class WelcomePage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      isLoading: false,
    };
  }

  componentDidMount() {
    this.setState({ isLoading: true });
    const proxyurl = "https://cors-anywhere.herokuapp.com/";
    const url =
      "mu url"
    fetch(proxyurl + url)
      .then((res) => res.json())
      .then((data) => this.setState({ data: data, isLoading: false }));
  }

  render() {
    const { data, isLoading } = this.state;
    if (isLoading) {
      return (
        <div className="pageLoading">
        <p>Loading...</p>
        </div>
      );
    }
    if (data.length === 0) {
      return <p> no data found</p>;
    }
    return (
      <div className="Welcome">
        <div className="Details">
          <p className="userID">
            <strong>
              Welcome {data.userForename} {data.userSurname}
            </strong>
          </p>
          <button className="SignOut">
            <Link to={{ pathname: "/LogIn" }}>Sign Out</Link>
          </button>
        </div>
        <div className="InfoBox">
          <p>
            <strong>Orders</strong>{" "}
          </p>
        </div>
        <div className="orderss">
          {data.orders.map((order, index) => (
          <ul className="orderBox" key={order.toString()+index}>
            <li className="orderLink">
              <Link
                to={{
                  pathname: "/OrderDetails",
                  state: {
                    orderNumber: order.rollNumber,
                    userID: this.state.userID,
                  },
                }}
                >
                Order number: {order.rollNumber}
              </Link>
            </li>
            <li> Customer name: {order.customerFullName}</li>
          </ul>
          ))}
        </div>
      </div>
    );
  }
}

export default WelcomePage;

welcome.page.test.js

import React, { useState } from 'react';
import renderer from 'react-test-renderer';
import {render, unmountComponentAtNode } from 'react-dom';
import WelcomePage from './WelcomePage';

let container = null;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
})

afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
})

test("test page renders as epxected when loading", () => {
  const userId= "123456";
  render(<WelcomePage location={userId}></UserWelcome>, container);
  expect(container.innerHTML).toContain("Loading...");
});

test("mock fetch call, empty responce from db should return no data", 
  async () => {
    const fakeResponse = [];
    const userId = "1234567";

    jest.spyOn(window, "fetch").mockImplementation(() => {
      const fetchResponse = {
        json: () => Promise.resolve(fakeResponse),
      };
      return Promise.resolve(fetchResponse);
    });

    await act(async () => {
      render(<WelcomePage location={userId} />, container);
    });

    expect(container.innerHTML).toContain("no data");

    window.fetch.mockRestore();
  }
);

test("mock fetch call, valid responce from db should return applications", async () => {
  //{ rates: { CAD: 1.42 } }
  const fakeResponse = [
    {
      data: {
        userId: 1234567,
        userForename: "Joe",
        userSurname: "Bloggs",
        orders: [
          {
            orderNumber: 10000000001,
            customerFullName: "Mrs Joes Bloggs",
            userId: 1234567
          },
        ]
      }
    }
  ];
  const userId = "1234567";

  jest.spyOn(window, "fetch").mockImplementation(() => {
    const fetchResponse = {
      json: () => Promise.resolve(fakeResponse)
    };
    return Promise.resolve(fetchResponse);
  });

  await act(async () => {
    render(<WelcomePage location={userId} />, container);
  });

  expect(container.textContent).toContain("Order number: 10000000001");

  window.fetch.mockRestore();
});

编辑 - 添加 package.json

{
  "name": "sm-app",
  "version": "0.1.0",
  "private": false,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "bootstrap": "^4.5.3",
    "moment": "^2.29.1",
    "prop-types": "^15.7.2",
    "react": "^17.0.1",
    "react-app-polyfill": "^2.0.0",
    "react-bootstrap": "^1.4.0",
    "react-dom": "^17.0.1",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.4.4",
    "react-spinners": "^0.9.0",
    "reactstrap": "^8.6.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all",
      "ie >= 9"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version",
      "ie 11"
    ]
  },
  "devDependencies": {
    "prettier": "2.1.2",
    "react-test-renderer": "^17.0.1"
  }
}

请分享 package.json 文件。 - Chandan
嗨@Chandan,我已更新我的问题以包括package.json。 - Reeves62
2个回答

2

测试用例

正如您所看到的,您传递了一个空数组,其中不包含订单。

test("mock fetch call, empty responce from db should return no data", 
  async () => {
    const fakeResponse = []; <-- empty array which does not contain orders
    const userId = "1234567";

    jest.spyOn(window, "fetch").mockImplementation(() => {
      const fetchResponse = {
        json: () => Promise.resolve(fakeResponse),
      };
      return Promise.resolve(fetchResponse);
    });

    await act(async () => {
      render(<WelcomePage location={userId} />, container);
    });

    expect(container.innerHTML).toContain("no data");

    window.fetch.mockRestore();
  }
);

由于尝试访问未定义的订单时数据为空,而undefined没有map函数。

{data.orders.map((order, index) => ( <-- problems here
    <ul className="orderBox" key={order.toString()+index}>
        <li className="orderLink">
            <Link
                to={{
                    pathname: "/OrderDetails",
                    state: {
                        orderNumber: order.rollNumber,
                        userID: this.state.userID,
                    },
                }}
            >
                Order number: {order.rollNumber}
            </Link>
        </li>
        <li> Customer name: {order.customerFullName}</li>
    </ul>
))}

您可以像这样进行更改:
{data && data.orders && data.orders.map((order, index) => ( <-- changes
    <ul className="orderBox" key={order.toString()+index}>
        <li className="orderLink">
            <Link
                to={{
                    pathname: "/OrderDetails",
                    state: {
                        orderNumber: order.rollNumber,
                        userID: this.state.userID,
                    },
                }}
            >
                Order number: {order.rollNumber}
            </Link>
        </li>
        <li> Customer name: {order.customerFullName}</li>
    </ul>
))}

注意:


  • 由于你将fakeResponse作为数组传递,因此您需要在WelcomePage.js中的fetch中更新setState为:
if (data && data[0] && data[0]['data']) {
    this.setState({
        data: data[0]['data'], <-- changes
        isLoading: false,
    });
}

或者将fakeResponse更改为:
const fakeResponse = {
  data: {
    userId: 1234567,
    userForename: "Joe",
    userSurname: "Bloggs",
    orders: [
      {
        orderNumber: 10000000001,
        customerFullName: "Mrs Joes Bloggs",
        userId: 1234567
      },
    ]
  }
};

设置状态为:

if (data && data['data']) {
    this.setState({
        data: data['data'], <-- changes
        isLoading: false,
    });
}

检查数据是否为空的条件:

if (Object.keys(data).length === 0) {
    return <p> no data found</p>;
}

1

fakeRespose 是一个对象数组。

  const fakeResponse = [
    {
      data: {
        userId: 1234567,
        userForename: "Joe",
        userSurname: "Bloggs",
        orders: [
          {
            orderNumber: 10000000001,
            customerFullName: "Mrs Joes Bloggs",
            userId: 1234567
          },
        ]
      }
    }
  ];

所以以下步骤通过。
    if (data.length === 0) {
      return <p> no data found</p>;
    }

但是data是一个数组,因此orders不存在。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接