6 unit-testing reactjs jestjs enzyme
我有一堆 API 调用,我想对其进行单元测试。据我所知,单元测试 API 调用并不涉及实际进行这些 API 调用。据我所知,您会模拟这些 API 调用的响应,然后测试 DOM 更改,但我目前正在努力做到这一点。我有以下代码:
应用程序.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;
Run Code Online (Sandbox Code Playgroud)
应用程序.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'])
})
Run Code Online (Sandbox Code Playgroud)
我试图在这里模拟 getApiData() 的结果,然后测试 DOM 更改(p 标记更改为结果)。上面的代码给了我错误:
无法监视 getApiData 属性,因为它不是函数;未定义给定代替
我如何访问该类函数?
编辑:
我已经调整了代码,但仍然遇到一些麻烦:
应用程序.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;
Run Code Online (Sandbox Code Playgroud)
apiController.js
export const API = {
apiCall() {
return fetch('/api')
.then(res => res.json())
}
}
Run Code Online (Sandbox Code Playgroud)
服务器.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}`))
Run Code Online (Sandbox Code Playgroud)
应用程序.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');
})
Run Code Online (Sandbox Code Playgroud)
我收到的错误是
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 () => {
Run Code Online (Sandbox Code Playgroud)
可重用的单元测试(希望如此):
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');
})
Run Code Online (Sandbox Code Playgroud)
ton*_*y g 12
不要嘲笑 API 库。最好是服务器响应的存根。如果您编写了一堆模拟 API 调用的测试,那么您就将应用程序的实现绑定到了测试。假设您不想使用fetch()但想要使用类似isomorphic-unfetchSSR 应用程序的东西?切换整个模拟测试套件将非常痛苦。
相反,请使用服务器存根库,例如nock或msw。将这些库视为 JSDOM,但适用于您的服务器。通过这种方式,您可以将测试套件绑定到后端而不是实现库。让我们重写您的示例以向您展示我的意思:
import React from 'react';
import nock from 'nock';
import { render, fireEvent, screen } from '@testing-library/react';
import App from './App';
it('displays user data', async () => {
const scope = nock('https://yoursite.com')
.get('/api')
.once()
.reply(200, {
data: 'response',
});
render(<App />)
fireEvent.click(screen.getByRole("button", {name: 'Make API call'}))
expect(await screen.findByText("response")).toBeInTheDocument();
})
Run Code Online (Sandbox Code Playgroud)
请查看我为深入探讨该主题而撰写的博客文章,测试进行 API 调用的组件。
小智 8
您无法访问,getApiData因为它是其他函数(闭包)内的私有函数,并且未公开到全局范围。这意味着global变量没有属性getApiData,而你得到了undefined given instead。
为此,您需要以某种方式导出此函数,我建议将其移动到不同的文件,但同样应该没问题。这是一个简单的例子:
export const API = {
getData() {
return fetch('/api').then(res => res.json())
}
}
Run Code Online (Sandbox Code Playgroud)
在你的组件中的某个地方:
API.getData().then(result => setText(result))
Run Code Online (Sandbox Code Playgroud)
并在测试中:
var apiFunc = jest.spyOn(API, 'getData').mockImplementationOnce(() => {
return Promise.resolve({
json: () => Promise.resolve(fakeUserResponse)
})
})
Run Code Online (Sandbox Code Playgroud)
还有其他方法可以实现这一目标,但也许这个就足够了。
我认为还会有一个问题。您正在使用const text = await getByTestId("ptag"),但getBy*react-testing-library 中的函数不是异步的(它们不会返回您可以等待解析的承诺),因此您的测试将失败,因为您不会等待模拟请求完成。相反,尝试findBy*您可以使用的此功能的版本await并确保承诺得到解决。
| 归档时间: |
|
| 查看次数: |
57358 次 |
| 最近记录: |