React-如何在Jest中对API调用进行单元测试? [英] React - how do I unit test an API call in Jest?

查看:41
本文介绍了React-如何在Jest中对API调用进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有很多我想进行单元测试的API调用.据我所知,单元测试API调用实际上并不涉及进行这些API调用.据我所知,您将模拟这些API调用的响应,然后对DOM更改进行测试,但是我目前仍在努力做到这一点.我有以下代码:

I have a bunch of API calls that I would like to unit test. As far as I know, unit testing API calls doesn't involve actually making those API calls. As far as I know you would simulate responses of those API calls and then test on the DOM changes however I'm currently struggling to do this. I have the following code:

App.js

function App() {

  const [text, setText] = useState("");

  function getApiData() {
        fetch('/api')
        .then(res => res.json())
        .then((result) => {
          console.log(JSON.stringify(result));
          setText(result); 
        })
      }

  return (
    <div className="App">
      {/* <button data-testid="modalButton" onClick={() => modalAlter(true)}>Show modal</button> */}
      <button data-testid="apiCall" onClick={() => getApiData()}>Make API call</button>
      <p data-testid="ptag">{text}</p>
    </div>
  );
}

export default App;

App.test.js

App.test.js

it('expect api call to change ptag', async () => {
  const fakeUserResponse = {'data': 'response'};
  var {getByTestId} = render(<App />)
  var apiFunc = jest.spyOn(global, 'getApiData').mockImplementationOnce(() => {
    return Promise.resolve({
      json: () => Promise.resolve(fakeUserResponse)
    })
  })


  fireEvent.click(getByTestId("apiCall"))
  const text = await getByTestId("ptag")
  expect(text).toHaveTextContent(fakeUserResponse['data'])
})

我试图在此处模拟getApiData()的结果,然后测试DOM的更改(p标记更改为结果).上面的代码给了我错误:

I'm trying to mock the result of getApiData() here and then test a DOM change (the p tag changes to the result). The above code gives me the error:

由于无法监视getApiData属性,因为它不是一个函数.未给定

Cannot spy the getApiData property because it is not a function; undefined given instead

如何访问该类函数?

我已经修改了代码,但仍然遇到一些麻烦:

I've adapted the code but I'm still having a bit of trouble:

App.js

function App() {

  const [text, setText] = useState("");

  async function getApiData() {
        let result = await API.apiCall()
        console.log("in react side " + result)
        setText(result['data'])
      }

  return (
    <div className="App">
      {/* <button data-testid="modalButton" onClick={() => modalAlter(true)}>Show modal</button> */}
      <button data-testid="apiCall" onClick={() => getApiData()}>Make API call</button>
      <p data-testid="ptag">{text}</p>
    </div>
  );
}

export default App;

apiController.js

apiController.js

export const API = {
    apiCall() {
        return fetch('/api')
        .then(res => res.json())
    }
}

Server.js

Server.js

const express = require('express')
const app = express()
const https = require('https')
const port = 5000

app.get('/api', (request, res) => {
    res.json("response")
})

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

App.test.js

App.test.js

import React from 'react';
import { render, shallow, fireEvent } from '@testing-library/react';
import App from './App';
import {API} from './apiController'
//import shallow from 'enzyme'

it('api call returns a string', async () => {
  const fakeUserResponse = {'data': 'response'};
  var apiFunc = jest.spyOn(API, 'apiCall').mockImplementationOnce(() => {
    return Promise.resolve({
      json: () => Promise.resolve(fakeUserResponse)
    })
  })
  var {getByTestId, findByTestId} = render(<App />)
  fireEvent.click(getByTestId("apiCall"))
  expect(await findByTestId("ptag")).toHaveTextContent('response');
})

我得到的错误是

expect(element).toHaveTextContent()

   Expected element to have text content:
     response
   Received:

     14 |   var {getByTestId, findByTestId} = render(<App />)
     15 |   fireEvent.click(getByTestId("apiCall"))
   > 16 |   expect(await findByTestId("ptag")).toHaveTextContent('response');
        |                                      ^
     17 | })
     18 | 
     19 | // it('api call returns a string', async () => {

可重复使用的单元测试(希望如此):

Reusable unit test (hopefully):

    it('api call returns a string', async () => {
      const test1 = {'data': 'response'};
       const test2 = {'data': 'wrong'}

      var apiFunc = (response) => jest.spyOn(API, 'apiCall').mockImplementation(() => {
        console.log("the response " + JSON.stringify(response))
        return Promise.resolve(response)
        })

      var {getByTestId, findByTestId} = render(<App />)

      let a = await apiFunc(test1);
      fireEvent.click(getByTestId("apiCall"))
      expect(await findByTestId("ptag")).toHaveTextContent('response');
      let b = await apiFunc(test2);
      fireEvent.click(getByTestId("apiCall"))
      expect(await findByTestId("ptag")).toHaveTextContent('wrong');

    })

推荐答案

您无法访问 getApiData ,因为它是其他函数(闭包)中的私有函数,并且没有公开给全局范围.这意味着 global 变量不具有属性 getApiData ,并且您将获得 undefined给定的.

You cannot access getApiData because it's a private function inside other function (a closure) and it's not exposed to the global scope. That means global variable does not have property getApiData, and you are getting undefined given instead.

要执行此操作,您需要以某种方式导出此功能,建议将其移至其他文件,但同样也可以.这是一个简单的示例:

To do this you need to export somehow this function, I would suggest by moving it to different file, but the same should be fine as well. Here's a simple example:

export const API = {
  getData() {
    return fetch('/api').then(res => res.json())
  }
}

组件中的某处:

API.getData().then(result => setText(result))

在测试中:

var apiFunc = jest.spyOn(API, 'getData').mockImplementationOnce(() => {
    return Promise.resolve({
      json: () => Promise.resolve(fakeUserResponse)
    })
  })

还有其他方法可以实现这一目标,但是也许这已经足够了.

There are other ways to achieve that, but maybe this one would be enough.

我认为还会有另外一个问题.您正在使用 const text = await getByTestId("ptag"),但是react-testing-library中的 getBy * 函数不是异步的(它们不返回可以等待解决),因此您的测试将失败,因为您不会等待模拟请求完成.相反,请尝试 findBy * 等待,并确保已兑现诺言.

And I think there would be one more problem. You are using const text = await getByTestId("ptag"), but getBy* functions from react-testing-library are not asynchronous (they do not return a promise you can wait to resolve), so your test will fail, as you wouldn't wait for a mock request to finish. Instead, try findBy* version of this function that you can await on and make sure promise is resolved.

这篇关于React-如何在Jest中对API调用进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆