错误:未捕获 [TypeError: 无法读取未定义的属性 'map'] 反应测试库

时间:2021-03-08 11:23:57

标签: reactjs typescript mocking react-testing-library

我在打字稿的反应测试库中编写了一些测试。测试成功通过,但控制台中有我无法破译的错误,有人可以帮助我吗?它发生在我在测试结束时更新我的​​测试之后。组件将值 ohio 输入一个字段并根据该值返回数据,我在测试中对此进行了模拟。

我所指的测试是此行运行后的 lands on the page and searches by state

 act(() => {
    fireEvent.click(getByText("Sort"));
  })
expect(axiosMock.get).toHaveBeenCalledTimes(2);

api 在 Sort 按钮提交时触发,测试通过,但留下了令人讨厌的错误。有人可以向我解释一下吗?

这是我的测试文件和我正在测试的组件:

组件 - Brewerlist.tsx :

import React, { useEffect, useState } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import queryString from 'query-string';
import beer from '../assets/beerMug.png';
import axios from 'axios';
import Pagination from './Pagination';

import '../App.css';

type Breweries = {
  id: number;
  name: string;
  brewery_type: string;
  state: string;
  city: string;
  country: string;
  street: string;
} 

function Brewerlist() {
  const [breweries, setBreweries] = useState<Breweries[]>([]);
  const [error, setError] = useState<any>(null);
  const [sortType, setSortType] = useState<string>("");
  const [sortValue, setSortValue] = useState<string>("");
  const [sortValueEmpty, setSortValueEmpty] = useState<boolean>(false);
  // const { response, error }: { response: object, error: string} = useFetch("https://api.openbrewerydb.org/breweries");

  const location = useLocation();
  const history = useHistory();
  const path = window.location.pathname;
  const url = queryString.parse(location.search);
  const pageNo = Number(url.page) || 1;

  const [currentPage, setCurrentPage] = useState(pageNo);
  const [ordersPerPage] = useState(10); 
  const indexOfLastOrder = currentPage * ordersPerPage;
  const indexOfFirstOrder = indexOfLastOrder - ordersPerPage;
  const currentOrders = breweries?.slice(indexOfFirstOrder, indexOfLastOrder);
  const paginate = (pageNumber: any) => setCurrentPage(pageNumber);

//fetch breweries
  const fetchData = async () => {
    try {
      const res = await axios.get("https://api.openbrewerydb.org/breweries?per_page=50");
      setBreweries(res?.data);
      } catch (error) {
      console.log(error);
    }
  }

  useEffect(() => {
    fetchData();
    history.push(`${path}?page=${currentPage}`);
  }, [currentPage, history, path]);

 //handlers
 const handleSort = (event: React.ChangeEvent<HTMLSelectElement>) => {
  setSortType(event.target.value)
 }

 const handleSortValue = (event: React.ChangeEvent<HTMLInputElement>) => {
  setSortValue(event.target.value)
 }

 const hideError = () => {
  setSortValueEmpty(false)
 }

 //refresh results
 const refreshResults = () => {
   fetchData();
 }

 //fetcher function
 const fetcher = async (api: string, type: string, val: string) => {
  const res = await axios.get(`${api}by_${type}=${val}`);
  setBreweries(res.data)
 }

 //fetch by term
 const fetchByType = async (type: string, value: string) => {
  if(sortValue === "") {
   setSortValueEmpty(true)
  }
  if(value && type === "state"){
    try {
        fetcher("https://api.openbrewerydb.org/breweries?", type, value)
      } catch (error) {
        console.log(error);
    }
  } 
  if(type === "brewery_type") {
    try { 
        fetcher("https://api.openbrewerydb.org/breweries?", "type", value)
      } catch (error) {
        console.log(error);
    }
  }
 }

  const mapBreweries = () => {
      return (
        <>
          {currentOrders.map((item: Breweries) => (
              <div className="item" key={item.id}>
                <ul>
                  <li><span className="title">Name:</span>{item.name}</li>
                  <li><span className="title">State:</span>{item.state}</li>
                  <li><span className="title">Brewery type:</span>{item.brewery_type}</li>
                  <li><span className="title">Street:</span>{item.street || "N/A"}</li>
                  <li><span className="title">Country:</span>{item.country}</li>
                  <li><span className="title">City:</span>{item.city}</li>
                </ul>
              </div>
            ))}
        </>
      )
  }

  return (
    <React.Fragment>
      <div className="header">
          <img src={beer} className="image" alt=""/>
            <h1>Sherman Brewery</h1>
          <img src={beer} className="image" alt=""/>
        </div>
        <div className="container">
        <div className="selectContainer">
          <label htmlFor="type">Sort by: </label>
          <select name="type" data-testid="select" onChange={handleSort}> 
            <option value="" selected defaultValue="Sort By" disabled hidden>Sort By</option>
            <option data-testid="type-option" value="state">State</option>
            <option data-testid="type-option" value="brewery_type">Brewery Type</option>
          </select>
          <label htmlFor="sortBy">Search by {sortType === "state" ? "State: " : "Brewery Type: "} </label>
          <input data-testid="search" disabled={sortType ? false : true} name="sortby" type="text" onFocus={hideError} onChange={handleSortValue} />
          <button disabled={sortType ? false : true} type="submit" onClick={() => fetchByType(sortType, sortValue)}>Sort</button>
          <button type="submit" onClick={refreshResults}>Refresh results</button>
          {sortValueEmpty ? <span className="error"> Please add a value</span> : null}
        </div>
            {mapBreweries()}
            <Pagination
              ordersPerPage={ordersPerPage}
              ordersAmount={breweries?.length}
              paginate={paginate}
            />
        </div>
      </React.Fragment>
    );
}

export default Brewerlist;

测试文件 - Brewerlist.test.js

import "@testing-library/jest-dom";
import "@testing-library/jest-dom/extend-expect";
import React from "react";
import axiosMock from "axios";
import Brewerlist from "../components/Brewerlist";
import { cleanup, render, fireEvent, waitFor } from "@testing-library/react";
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { fixtures } from './data'; 
import { ohio } from './ohioData'; 
import axios from "./axios";
import { act } from "react-dom/test-utils";

afterEach(cleanup);

jest.mock("axios");

let data = fixtures;

let url = "https://api.openbrewerydb.org/breweries?per_page=50";

it("lands on the page and displays data", async () => {
  axiosMock.get.mockResolvedValueOnce({
    data
  });

  const history = createMemoryHistory()
  const { getByText, getByLabelText } = render(<Router history={history}><Brewerlist /></Router>);

  await waitFor(() => expect(getByText("Sherman Brewery")).toBeInTheDocument());  
  await waitFor(() => expect(getByText("Avondale Brewing Co")).toBeInTheDocument());  
  expect(axiosMock.get).toHaveBeenCalledTimes(1)
});

it("lands on the page and searches by state", async () => {
  axiosMock.get.mockResolvedValueOnce({
    data
  });

  const history = createMemoryHistory()
  const { getByText, getByLabelText, getAllByTestId, getByTestId } = render(<Router history={history}><Brewerlist /></Router>);

  await waitFor(() => expect(getByText("Sherman Brewery")).toBeInTheDocument());  
  await waitFor(() => expect(getByText("Avondale Brewing Co")).toBeInTheDocument());  

  fireEvent.change(getByTestId("select"), { target: { value: "state" } });
  let typeOptions = getAllByTestId("type-option");
  expect(typeOptions[0].selected).toBeTruthy();
  expect(typeOptions[1].selected).toBeFalsy();

  expect(getByText("Search by State:")).toBeInTheDocument();

  const searchInput = getByTestId("search");

  fireEvent.change(searchInput, { target: { value: "ohio" } });
  expect(searchInput.value).toBe("ohio");
  
  axiosMock.get.mockResolvedValueOnce({
    ohio
  });

  act(() => {
    fireEvent.click(getByText("Sort"));
  })
  expect(axiosMock.get).toHaveBeenCalledTimes(2);

  await waitFor(() => expect(getByText("Magic City Brewing Company")).toBeInTheDocument());  
})

axios.js - 用于模拟 api

export default {
  get: jest.fn().mockResolvedValue({
    data: [{}]
  }),
};

测试数据:

export const fixtures = [
    {
       "id":2,
       "name":"Avondale Brewing Co",
       "brewery_type":"micro",
       "street":"201 41st St S",
       "address_2":null,
       "address_3":null,
       "city":"Birmingham",
       "state":"Alabama",
       "county_province":null,
       "postal_code":"35222-1932",
       "country":"United States",
       "longitude":"-86.774322",
       "latitude":"33.524521",
       "phone":"2057775456",
       "website_url":"http://www.avondalebrewing.com",
       "updated_at":"2018-08-23T23:19:57.825Z",
       "created_at":"2018-07-24T01:32:47.255Z"
    },
]
export const ohio = [
  {
     "id":2,
     "name":"Magic City Brewing Company",
     "brewery_type":"micro",
     "street":"201 41st St S",
     "address_2":null,
     "address_3":null,
     "city":"Birmingham",
     "state":"Ohio",
     "county_province":null,
     "postal_code":"35222-1932",
     "country":"United States",
     "longitude":"-86.774322",
     "latitude":"33.524521",
     "phone":"2057775456",
     "website_url":"http://www.avondalebrewing.com",
     "updated_at":"2018-08-23T23:19:57.825Z",
     "created_at":"2018-07-24T01:32:47.255Z"
  },
]

运行 lands on the page and searches by state 测试时返回的错误

    Error: Uncaught [TypeError: Cannot read property 'map' of undefined]
        at reportException (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
        at innerInvokeEventListeners (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:333:9)
        at invokeEventListeners (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
        at HTMLUnknownElementImpl._dispatch (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
        at HTMLUnknownElementImpl.dispatchEvent (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
        at HTMLUnknownElement.dispatchEvent (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
        at Object.invokeGuardedCallbackDev (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:3994:16)
        at invokeGuardedCallback (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:4056:31)
        at beginWork$1 (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:23964:7)
        at performUnitOfWork (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:22779:12) TypeError: Cannot read property 'map' of undefined
        at mapBreweries (/Users/nathan/Desktop/work/breweryapp/src/components/Brewerlist.tsx:108:26)
        at Brewerlist (/Users/nathan/Desktop/work/breweryapp/src/components/Brewerlist.tsx:145:14)
        at renderWithHooks (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:14985:18)
        at updateFunctionComponent (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:17356:20)
        at beginWork (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:19063:16)
        at HTMLUnknownElement.callCallback (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:3945:14)
        at HTMLUnknownElement.callTheUserObjectsOperation (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
        at innerInvokeEventListeners (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:318:25)
        at invokeEventListeners (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
        at HTMLUnknownElementImpl._dispatch (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
        at HTMLUnknownElementImpl.dispatchEvent (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
        at HTMLUnknownElement.dispatchEvent (/Users/nathan/Desktop/work/breweryapp/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
        at Object.invokeGuardedCallbackDev (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:3994:16)
        at invokeGuardedCallback (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:4056:31)
        at beginWork$1 (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:23964:7)
        at performUnitOfWork (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:22779:12)
        at workLoopSync (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:22707:5)
        at renderRootSync (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:22670:7)
        at performSyncWorkOnRoot (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:22293:18)
        at /Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:11327:26
        at unstable_runWithPriority (/Users/nathan/Desktop/work/breweryapp/node_modules/scheduler/cjs/scheduler.development.js:646:12)
        at runWithPriority$1 (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:11276:10)
        at flushSyncCallbackQueueImpl (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:11322:9)
        at flushSyncCallbackQueue (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:11309:3)
        at scheduleUpdateOnFiber (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:21893:9)
        at setBreweries (/Users/nathan/Desktop/work/breweryapp/node_modules/react-dom/cjs/react-dom.development.js:16139:5)
        at fetcher (/Users/nathan/Desktop/work/breweryapp/src/components/Brewerlist.tsx:81:3)

      at VirtualConsole.<anonymous> (node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
      at reportException (node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:28)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:333:9)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)

  console.error
    The above error occurred in the <Brewerlist> component:

        at Brewerlist (/Users/nathan/Desktop/work/breweryapp/src/components/Brewerlist.tsx:25:37)
        at Router (/Users/nathan/Desktop/work/breweryapp/node_modules/react-router/cjs/react-router.js:99:30)

    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

      at logCapturedError (node_modules/react-dom/cjs/react-dom.development.js:20085:23)
      at update.callback (node_modules/react-dom/cjs/react-dom.development.js:20118:5)
      at callCallback (node_modules/react-dom/cjs/react-dom.development.js:12318:12)
      at commitUpdateQueue (node_modules/react-dom/cjs/react-dom.development.js:12339:9)
      at commitLifeCycles (node_modules/react-dom/cjs/react-dom.development.js:20736:11)
      at commitLayoutEffects (node_modules/react-dom/cjs/react-dom.development.js:23426:7)
      at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:3945:14)

0 个答案:

没有答案