【问题标题】:React component isn't re rendering the fetch data in unit testReact 组件没有在单元测试中重新渲染获取数据
【发布时间】:2021-07-11 19:24:12
【问题描述】:

我正在尝试使用虚拟子组件测试上下文。

虚拟组件

function DefaultMockComponent() {

  const values = useContext(CurrencyContext)
  const valuesItems = Object.keys(values).map(value => {
    if (typeof values[value] === 'function') {
      return <div key={value} data-testid={value}>{value + '()'}</div>
    } else {
      return <div key={value} data-testid={value}>{values[value]}</div>
    }
  })

  return(
    <>
      {valuesItems}
    </>
  )
}

上下文单元测试

  // This test don't re render with the new data from fetch
  it('should pass the correct values', async () => {

    // This mock FAILS (see edit 2)
    fetch
      .mockResponse(req => {
        if (/.*\/ticker\/.*/.test(req.url)) {
          return new Promise(() => ({ body: JSON.stringify(tickerResponse) }))

        } else if (/.*\/day-summary\/.*/.test(req.url)) {
          return new Promise(() => ({ body: JSON.stringify(summaryResponse) }))

        }
      })

    render(
      <CurrencyProvider>
        <DefaultMockComponent />
      </CurrencyProvider>
    )

    await waitFor(() => expect(screen.getByTestId('currency').textContent).toBe("btc"))
    await waitFor(() => expect(screen.getByTestId('volBRL').textContent).toBe("41198719.10154287"))
    await waitFor(() => expect(screen.getByTestId('closing').textContent).toBe("326900.00666999"))
    await waitFor(() => expect(screen.getByTestId('sell').textContent).toBe("360000.00000000"))
    await waitFor(() => expect(screen.getByTestId('buy').textContent).toBe("359999.99006000"))
    await waitFor(() => expect(screen.getByTestId('last').textContent).toBe("359999.99006000"))
    await waitFor(() => expect(screen.getByTestId('vol').textContent).toBe("259.88030295"))
    await waitFor(() => expect(screen.getByTestId('low').textContent).toBe("353684.14000000"))
    await waitFor(() => expect(screen.getByTestId('high').textContent).toBe("380000.00000000"))
  });

这里 fetch 是模拟的。我正在阅读 MockComponent 信息并将其与响应对象信息进行比较。我的问题是 MockComponent 仅使用默认上下文状态值呈现,如您所见:

渲染的模拟组件

    <body>
      <div>
        <div
          data-testid="high"
        >
          0
        </div>
        <div
          data-testid="low"
        >
          0
        </div>
        <div
          data-testid="vol"
        >
          0
        </div>
        <div
          data-testid="last"
        >
          0
        </div>
        <div
          data-testid="buy"
        >
          0
        </div>
        <div
          data-testid="sell"
        >
          0
        </div>
        <div
          data-testid="closing"
        >
          0
        </div>
        <div
          data-testid="volBRL"
        >
          0
        </div>
        <div
          data-testid="currency"
        >
          btc
        </div>
        <div
          data-testid="setCurrency"
        >
          setCurrency()
        </div>
      </div>
    </body>

这很奇怪,因为 我的赛普拉斯 E2E 测试工作正常。如何使 DefaultMockComponent 呈现从 fetch 返回的数据?

编辑 1

CurrencyContext 的CurrencyProvider

export default function CurrencyProvider({children}) {
  const [currency, setCurrency] = useState('btc')
  const [high, setHigh] = useState('0')
  const [low, setLow] = useState('0')
  const [vol, setVol] = useState('0')
  const [last, setLast] = useState('0')
  const [buy, setBuy] = useState('0')
  const [sell, setSell] = useState('0')
  const [closing, setClosing] = useState('0')
  const [volBRL, setVolBRL] = useState('0')

  useEffect(update, [currency])

  function update() {
    const ticker = `https://www.mercadobitcoin.net/api/${currency}/ticker/`

    fetch(ticker)
      .then(response => response.json())
      .then(data => {
        setHigh(data.ticker.high)
        setLow(data.ticker.low)
        setVol(data.ticker.vol)
        setLast(data.ticker.last)
        setBuy(data.ticker.buy)
        setSell(data.ticker.sell)
      })

    const date = new Date()
    date.setDate(date.getDate() - 1)

    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()

    const summary = `https://www.mercadobitcoin.net/api/${currency}/day-summary/${year}/${month}/${day}/`

    fetch(summary)
      .then(response => response.json())
      .then(data => {
        setClosing(data.closing)
        setVolBRL(data.volume)
      })
  }

  return(
    <CurrencyContext.Provider value={{
      high,
      low,
      vol,
      last,
      buy,
      sell,
      closing,
      volBRL,
      currency,
      setCurrency
    }}>
      {children}
    </CurrencyContext.Provider>
  )
}

编辑 2

感谢lissettdm。现在 fetch 函数没有失败,但是值仍然没有更新,而且我收到“测试中的 CurrencyProvider 更新没有包含在 act(...) 中”警告。遵循有效的模拟代码:

没有 jest-fetch-mock 的模拟(作为 lissettdm 的答案)

    const fetchSpy = jest.spyOn(window, "fetch").mockImplementation((req) =>
      Promise.resolve({
        json: () => {
          if (/.*\/ticker\/.*/.test(req)) {
            return tickerResponse
          } else if (/.*\/day-summary\/.*/.test(req)) {
            return summaryResponse
          }
        },
      })
    );

用 jest-fetch-mock 模拟

fetch
      .mockResponse(req => {
        if (/.*\/ticker\/.*/.test(req.url)) {
          return Promise.resolve({ body: JSON.stringify(tickerResponse) })

        } else if (/.*\/day-summary\/.*/.test(req.url)) {
          return Promise.resolve({ body: JSON.stringify(summaryResponse) })

        }
      })

【问题讨论】:

  • 不完全理解您的代码,但在我看来,您从未更改 CurrencyContext 中的任何内容,您只调用值。如果任何状态都没有变化,则不会导致重新渲染。
  • 感谢您的评论@JoseRodrigues。我将编辑问题以包含我的上下文,因为那里的状态发生了变化。让我感到奇怪的是,相同的代码在可视化测试中有效,而在单元测试中却失败了。
  • 应该useEffect(update, [currency]) 去追求实际的更新功能吗?
  • 因为我已经用function关键字声明了update,所以我什么时候使用都没关系。我只是为了检查而进行了更改,但它失败了。谢谢。

标签: reactjs unit-testing


【解决方案1】:

问题:

您只能看到默认上下文值,因为 fetch 函数失败。第一个then 期望响应对象包含json 函数,但缺少该部分。

解决办法:

您需要将json 函数添加到您的模拟实现中:

const fetchSpy = jest.spyOn(window, "fetch").mockImplementationOnce((req) =>
  Promise.resolve({
    json: () => {
     if (/.*\/ticker\/.*/.test(req.url)) {
        return new Promise(() => ({ body: JSON.stringify(tickerResponse) }))
     } else if (/.*\/day-summary\/.*/.test(req.url)) {
        return new Promise(() => ({ body: JSON.stringify(summaryResponse) }))
     }
   },
 }));
 //--> check if fetch was called
 expect(fetchSpy).toHaveBeenCalled();

关于此警告:

测试中对 CurrencyProvider 的更新未包含在 act(...)" 警告

您的测试可能会在没有刷新状态的情况下结束。我认为您应该使用findBy 方法,它们是getBy 查询和waitFor 的组合

 const currency = await screen.getByTestId('currency');
 expect(currency.textContent).toBe("btc");

【讨论】:

  • 非常感谢您的回答。 “获取”中有一个错字。请检查我在问题中的编辑 2。
  • 谢谢,我修好了
  • 您使用什么库进行测试?
  • 我目前正在使用带有 jest 和 @testing-library/react 的 create-react-app。
  • 就是这样。起初 fetch 模拟失败,然后测试提前终止。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2017-04-19
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 2020-01-22
  • 2022-12-19
  • 2021-06-07
  • 1970-01-01
相关资源
最近更新 更多