日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

react dispatch_React测试的那些事(三) React Hook 测试实例

發(fā)布時間:2023/12/4 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 react dispatch_React测试的那些事(三) React Hook 测试实例 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

useReducer

測試 useReducer 首先需要在組件中用 actions 和 reducers ,代碼如下。

Reducer

import * as ACTIONS from './actions'export const initialState = {stateprop1: false, }export const Reducer1 = (state = initialState, action) => {switch(action.type) {case "SUCCESS":return {...state,stateprop1: true,}case "FAILURE":return {...state,stateprop1: false,}default:return state} }

actions

export const SUCCESS = {type: 'SUCCESS' }export const FAILURE = {type: 'FAILURE' }

我們先寫個簡單的,只用action,不用action creators 代碼如下:

import React, { useReducer } from 'react'; import * as ACTIONS from '../store/actions' import * as Reducer from '../store/reducer'const TestHookReducer = () => {const [reducerState, dispatch] = useReducer(Reducer.Reducer1, Reducer.initialState)const dispatchActionSuccess = () => {dispatch(ACTIONS.SUCCESS)}const dispatchActionFailure = () => {dispatch(ACTIONS.FAILURE)}return (<div><div>{reducerState.stateprop1? <p>stateprop1 is true</p>: <p>stateprop1 is false</p>}</div><button onClick={dispatchActionSuccess}>Dispatch Success</button></div>) }export default TestHookReducer;

這就是一個簡單的組件,通過dispatching 名為SUCCESS 的動作,把 stateprop1從 false 變成 true 。這是一個超基本的測試,保證initial state是我們想要的結(jié)果。

你可能想說,測試reducer就是測試實現(xiàn)的具體細節(jié),不建議這樣做的呀?但在實踐中發(fā)現(xiàn)這種測試還是很必要的,它也算作一種單元測試。

這個簡單的例子里面測試reducers看起來不是什么大事。當狀態(tài)更復雜的情況不進行測試會產(chǎn)生很多問題。所以請務必對actions和reducers進行測試。

~useContext~

下面我們設(shè)想另一個場景,一個子組件能夠更新父組件的上下文環(huán)境的state。聽起來有點繞,實際上很簡單。

首先初始化一個Context對象

import React from 'react';const Context = React.createContext()export default Context

父組件中提供Context.provider。傳遞給Provider的值是 App.js組件中setState函數(shù) 和state值

import React, { useState } from 'react'; import TestHookContext from './components/react-testing-lib/test_hook_context';import Context from './components/store/context';const App = () => {const [state, setState] = useState("Some Text")const changeText = () => {setState("Some Other Text")}return (<div className="App"><h1> Basic Hook useContext</h1><Context.Provider value={{changeTextProp: changeText,stateProp: state}} ><TestHookContext /></Context.Provider></div>); }export default App;

子組件非常簡單:展示在父組件中初始化的文字,當點擊按鈕時執(zhí)行setState函數(shù)。

import React, { useContext } from 'react';import Context from '../store/context';const TestHookContext = () => {const context = useContext(Context)return (<div><button onClick={context.changeTextProp}>Change Text</button><p>{context.stateProp}</p></div>) }export default TestHookContext;

父組件中狀態(tài)進行了初始化和改變。我們只是用setState函數(shù)將狀態(tài)值傳遞給子組件。所以我們?nèi)缦逻M行測試

import React from 'react'; import ReactDOM from 'react-dom'; import TestHookContext from '../test_hook_context.js'; import {act, render, fireEvent, cleanup} from '@testing-library/react'; import App from '../../../App'import Context from '../../store/context';afterEach(cleanup)it('Context value is updated by child component', () => {const { container, getByText } = render(<App><Context.Provider><TestHookContext /></Context.Provider></App>);expect(getByText(/Some/i).textContent).toBe("Some Text")fireEvent.click(getByText("Change Text"))expect(getByText(/Some/i).textContent).toBe("Some Other Text") })

雖然我們在render函數(shù)中寫了<Context.Provider/>和 <TestHookContext />,但實際上并沒必要。寫是為了容易理解代碼,不寫呢程序還是會運行

const { container, getByText } = render(<App/>)

~一點思考~

讓我們來回想下整個過程。所有的context state包含在父組件中,所以我們實際上測試的就是父組件,只是看起來像在用 useContext 測試著子組件而已。由于mount/render能渲染子組件(shallow不會渲染子組件),所以 <Context.Provider/> 和 <TestHookContext />這倆子組件被自動渲染出來了。

表單中的受控組件

受控組件代表著這個表單的state并沒有掌握在組件手里而在React的狀態(tài)中。每個按鍵都把輸入的內(nèi)容通過 onChange 保存在了React狀態(tài)里。

測試這樣的組件會比之前的復雜一些。

先看一個非常基本表單的組件

import React, { useState } from 'react';const HooksForm1 = () => {const [valueChange, setValueChange] = useState('')const [valueSubmit, setValueSubmit] = useState('')const handleChange = (event) => (setValueChange(event.target.value));const handleSubmit = (event) => {event.preventDefault();setValueSubmit(event.target.text1.value)};return (<div><h1> React Hooks Form </h1><form data-testid="form" onSubmit={handleSubmit}><label htmlFor="text1">Input Text:</label><input id="text1" onChange={handleChange} type="text" /><button type="submit">Submit</button></form><h3>React State:</h3><p>Change: {valueChange}</p><p>Submit Value: {valueSubmit}</p><br /></div>) }export default HooksForm1;

組件很簡單,包含form中基本的change、submit操作,form的data-testid=form可以作為查詢的ID值。

測試

import React from 'react'; import ReactDOM from 'react-dom'; import HooksForm1 from '../test_hook_form.js'; import {render, fireEvent, cleanup} from '@testing-library/react';afterEach(cleanup)//testing a controlled component form. it('Inputing text updates the state', () => {const { getByText, getByLabelText } = render(<HooksForm1 />);expect(getByText(/Change/i).textContent).toBe("Change: ")fireEvent.change(getByLabelText("Input Text:"), {target: {value: 'Text' } } )expect(getByText(/Change/i).textContent).not.toBe("Change: ")})it('submiting a form works correctly', () => {const { getByTestId, getByText } = render(<HooksForm1 />);expect(getByText(/Submit Value/i).textContent).toBe("Submit Value: ")fireEvent.submit(getByTestId("form"), {target: {text1: {value: 'Text' } } })expect(getByText(/Submit Value/i).textContent).not.toBe("Submit Value: ")})
  • 由于input元素還沒有輸入值,我們用getByLabelText()函數(shù)找到它。這也符合我們的測試原則,因為用戶再輸入值之前也看的label呀。
  • 我們用.change()代替了.click()事件,也可以用{target: {value: "Text"}}的方式傳遞假數(shù)據(jù)。
  • 表單用event.target.value取值,這就是我們模擬事件時傳參的對象。
  • 由于我們并不確定用戶輸入的是什么內(nèi)容,可以用.not確保渲染的內(nèi)容確實變了。
  • 我們可以用相似方法測試表單的提交。不同之處為 .submit()傳這串信息{target: {text1: {value: 'Text'}}} (input元素的id是text1)
  • 在這里用data-testid="form"匹配到我們的form元素,因為這是最優(yōu)的辦法了。

以上,介紹了獲取用戶提交表單的數(shù)據(jù)的方法。是不是和之前的例子相差不大?如果沒問題的話,接下來看點更復雜的吧。

useEffect 和 API請求

接下來我們看看如何測試useEffect hook 和 API請求(axios) ,與之前的都不太一樣。

先假設(shè)有一個url從 根組件傳遞到子組件

...<TestAxios url='https://jsonplaceholder.typicode.com/posts/1' />...

簡單的發(fā)API請求并把結(jié)果保存在本地state的組件

import React, { useState, useEffect } from 'react'; import axios from 'axios';const TestAxios = (props) => {const [state, setState] = useState()useEffect(() => {axios.get(props.url).then(res => setState(res.data))}, [])return (<div><h1> Axios Test </h1>{state? <p data-testid="title">{state.title}</p>: <p>...Loading</p>}</div>) }export default TestAxios;
  • 標題的placeholder顯示的內(nèi)容是從一個三目運算符中得來的。
  • 本例仍需用 data-testid屬性 ,雖然用戶看不到也接觸不到它,但在API返回數(shù)據(jù)之前不知道是什么值,所以靠此屬性來匹配到元素。

這里我們用mock數(shù)據(jù)(Mock是在測試中常用的模擬方法,比如用mock API 模擬真實的請求)因為用真實的數(shù)據(jù)進行測試的話,拖慢了測試的速度,有時接口會有意外的錯誤,測試數(shù)據(jù)會弄亂數(shù)據(jù)庫等問題。

~引入依賴~

import React from 'react'; import ReactDOM from 'react-dom'; import TestAxios from '../test_axios.js'; import {act, render, fireEvent, cleanup, waitForElement} from '@testing-library/react';import axiosMock from "axios";

有句之前沒介紹過的引入 import axiosMock from "axios";它不是說從axios庫中引入axiosMock,而是mock了axios這個庫。

~mock~

是不是很奇怪,它怎么做到的?它用到了Jest提供的模擬功能。

首先我們創(chuàng)建一個__mocks__文件夾,位置與__test__相鄰。

在__mocks__文件夾中創(chuàng)建一個 axios.js文件,它就是我們偽造的axios庫。在我們偽造的axios庫中加入jest mock 函數(shù)。嗯?這是什么函數(shù)?在jest環(huán)境中無需實現(xiàn)具體的請求邏輯,直接用這個模擬函數(shù)返回數(shù)據(jù)即可。喏~ 看個例子

export default {get: jest.fn(() => Promise.resolve({ data: {} }) ) };
  • 此處簡單的示例中,偽造的get函數(shù)就是一個JS對象;
  • get就是key值,value就是 mock 函數(shù)
  • 就像一個 axiosAPI請求,我們得到了一個promise
  • 這個例子中沒有填寫任何返回數(shù)據(jù),接下來我們會加上返回值

~加入mock返回值的測試~

//imports ...afterEach(cleanup)it('Async axios request works', async () => {axiosMock.get.mockResolvedValue({data: { title: 'some title' } })const url = 'https://jsonplaceholder.typicode.com/posts/1'const { getByText, getByTestId, rerender } = render(<TestAxios url={url} />);expect(getByText(/...Loading/i).textContent).toBe("...Loading")const resolvedEl = await waitForElement(() => getByTestId("title"));expect((resolvedEl).textContent).toBe("some title")expect(axiosMock.get).toHaveBeenCalledTimes(1);expect(axiosMock.get).toHaveBeenCalledWith(url);})
  • 我們做的第一件事,調(diào)用了偽造的 axios get request ,偽造請求結(jié)果我們用的是jest提供的方法mockResolvedValue ,這個函數(shù)做的和它的函數(shù)名一樣,它像axios那樣 resolves一個promise 。
  • mockResolvedValue需要在render之前進行調(diào)用,否則test不會生效。因為它是我們偽造的 axios,當執(zhí)行import axios from 'axios'; 時,會導入我們偽造的axios,并把組件中用到的axios全部替換掉。
  • 接下來,在promise返回前,一直處于加載狀態(tài),UI上出現(xiàn)...Loading。
  • waitForElement()函數(shù)我們之前都沒見過,它會等到promise返回結(jié)果后才跳到下一個斷言。
  • await 、 async 他們的用法與正常的非測試場景是一樣的。
  • 當解析出DOM后,UI會出現(xiàn)我們偽造的mock返回值“some title”
  • 接下來我們要確保請求只調(diào)用了一次和url的正確性(雖然沒用到這個URL我們也要這么測試一下)

以上就是如何對axios的請求進行測試,下面一章我們會講到如何用cypress進行e to e測試。

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的react dispatch_React测试的那些事(三) React Hook 测试实例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。