[React] Ensure all React useEffect Effects Run Synchronously in Tests with react-testing-library
Thanks to react-testing-library our tests are free of implementation details, so when we refactor components to hooks we generally don't need to make any changes to our tests. However, useEffectis slightly different from componentDidMount in that it's actually executed asynchronously after the render has taken place. So all of our query tests which relied on the HTTP requests being sent immediately after render are failing. Let's use the flushEffects utility from react-testing-library to ensure that the pending effect callbacks are run before we make assertions.
Component code:
import {useContext, useReducer, useEffect} from 'react'
import * as GitHub from '../../../github-client'
function Query ({query, variables, children, normalize = data => data}) {
const client = useContext(GitHub.Context)
const defaultState = {loaded: false, fetching: false, data: null, error: null}
const [state, setState] = useReducer(
(state, newState) => ({...state, ...newState}),
defaultState)
useEffect(() => {
setState({fetching: true})
client
.request(query, variables)
.then(res =>
setState({
data: normalize(res),
error: null,
loaded: true,
fetching: false,
}),
)
.catch(error =>
setState({
error,
data: null,
loaded: false,
fetching: false,
}),
)
}, [query, variables]) // trigger the effects when 'query' or 'variables' changes
return children(state)
}
export default Query
Test Code:
import {render as rtlRender, wait, flushEffects} from 'react-testing-library'
import React from 'react'
import {render as rtlRender, wait, flushEffects} from 'react-testing-library'
import * as GitHubClient from '../../../../github-client'
import Query from '../query' const fakeResponse = {fakeData: {}}
const fakeClient = {request: jest.fn(() => Promise.resolve(fakeResponse))} beforeEach(() => {
fakeClient.request.mockClear()
}) function renderQuery({
client = fakeClient,
children = jest.fn(() => null),
query = '',
variables = {},
normalize,
...options
} = {}) {
const props = {query, variables, children, normalize}
const utils = rtlRender(
<GitHubClient.Provider client={client}>
<Query {...props} />
</GitHubClient.Provider>,
options,
)
return {
...utils,
rerender: options =>
renderQuery({
container: utils.container,
children,
query,
variables,
normalize,
...options,
}),
client,
query,
variables,
children,
}
} test('query makes requests to the client on mount', async () => {
const {children, client, variables, query} = renderQuery()
flushEffects();
expect(children).toHaveBeenCalledTimes()
expect(children).toHaveBeenCalledWith({
data: null,
error: null,
fetching: true,
loaded: false,
})
expect(client.request).toHaveBeenCalledTimes()
expect(client.request).toHaveBeenCalledWith(query, variables) children.mockClear()
await wait() expect(children).toHaveBeenCalledTimes()
expect(children).toHaveBeenCalledWith({
data: fakeResponse,
error: null,
fetching: false,
loaded: true,
})
}) test('does not request if rerendered and nothing changed', async () => {
const {children, client, rerender} = renderQuery()
flushEffects();
await wait()
children.mockClear()
client.request.mockClear()
rerender()
flushEffects();
await wait()
expect(client.request).toHaveBeenCalledTimes()
expect(children).toHaveBeenCalledTimes() // does still re-render children.
}) test('makes request if rerendered with new variables', async () => {
const {client, query, rerender} = renderQuery({
variables: {username: 'fred'},
})
flushEffects();
await wait()
client.request.mockClear()
const newVariables = {username: 'george'}
rerender({variables: newVariables})
flushEffects();
await wait()
expect(client.request).toHaveBeenCalledTimes()
expect(client.request).toHaveBeenCalledWith(query, newVariables)
}) test('makes request if rerendered with new query', async () => {
const {client, variables, rerender} = renderQuery({
query: `query neat() {}`,
})
flushEffects();
await wait()
client.request.mockClear()
const newQuery = `query nice() {}`
rerender({query: newQuery})
flushEffects();
await wait()
expect(client.request).toHaveBeenCalledTimes()
expect(client.request).toHaveBeenCalledWith(newQuery, variables)
}) test('normalize allows modifying data', async () => {
const normalize = data => ({normalizedData: data})
const {children} = renderQuery({normalize})
flushEffects();
await wait()
expect(children).toHaveBeenCalledWith({
data: {normalizedData: fakeResponse},
error: null,
fetching: false,
loaded: true,
})
})
[React] Ensure all React useEffect Effects Run Synchronously in Tests with react-testing-library的更多相关文章
- React Hook:使用 useEffect
React Hook:使用 useEffect 一.描述 二.需要清理的副作用 1.在 class 组件中 2.使用 effect Hook 的示例 1.useEffect 做了什么? 2.为什么在组 ...
- React Hooks --- useState 和 useEffect
首先要说的一点是React Hooks 都是函数,使用React Hooks,就是调用函数,只不过不同的Hooks(函数)有不同的功能而已.其次,React Hooks只能在函数组件中使用,函数组件也 ...
- React中useLayoutEffect和useEffect的区别
重点: 1.二者函数签名相同,调用方式是一致的 2. 怎么简单进行选择: 无脑选择useEffect,除非运行效果和你预期的不一致再试试useLayoutEffect 区别详解:useEffect是异 ...
- React Native Android原生模块开发实战|教程|心得|怎样创建React Native Android原生模块
尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://blog.csdn.net/fengyuzhengfan/article/details/54691503) 告诉大家一个好消息. ...
- 如何使用TDD和React Testing Library构建健壮的React应用程序
如何使用TDD和React Testing Library构建健壮的React应用程序 当我开始学习React时,我努力的一件事就是以一种既有用又直观的方式来测试我的web应用程序. 每次我想测试它时 ...
- [React] Safely setState on a Mounted React Component through the useEffect Hook
In the class version of this component, we had a method called safeSetState which would check whethe ...
- react创建项目后运行npm run eject命令将配置文件暴露出来时报错解决方法
最近在用create-react-app创建项目,因要配置各种组件,比如babel,antd等, 需要运行npm run eject命令把项目的配置文件暴露出来,但是还是一如既然碰到报错,因为是在本地 ...
- react+ant design 项目执行yarn run eject 命令后无法启动项目
如何将内建配置全部暴露? 使用create-react-app结合antd搭建的项目中,项目目录没有该项目所有的内建配置, 1.执行yarn run eject 执行该命令后,运行项目yarn sta ...
- React 16 源码瞎几把解读 【二】 react组件的解析过程
一.一个真正的react组件编译后长啥样? 我们瞎几把解读了react 虚拟dom对象是怎么生成的,生成了一个什么样的解构.一个react组件不光由若干个这些嵌套的虚拟dom对象组成,还包括各种生命周 ...
随机推荐
- python3 爬虫之爬取安居客二手房资讯(第一版)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Author;Tsukasa import requests from bs4 import Beau ...
- Linux上用Docker部署Net Core项目
前提:本地配置好Docker环境1.构建Net Core镜像 docker pull microsoft/dotnet 2.新建一个DockerFile文件并填充内容 #基于 `microsoft/d ...
- getattr(sys.modules[__name__], func_name)
有时我们需要将一个文件的信息(类.函数及变量)保存到文件,我们不能直接保存函数对象,而是将其转化为fn.__name__,问题来了,当我们想通过读取文件的形式重新配置这些类.函数时,该如何把这些字符串 ...
- java8新特性——并行流与顺序流
在我们开发过程中,我们都知道想要提高程序效率,我们可以启用多线程去并行处理,而java8中对数据处理也提供了它得并行方法,今天就来简单学习一下java8中得并行流与顺序流. 并行流就是把一个内容分成多 ...
- bzoj1093 [ZJOI2007]最大半联通子图 缩点 + 拓扑序
最大半联通子图对应缩点后的$DAG$上的最长链 复杂度$O(n + m)$ #include <cstdio> #include <cstring> #include < ...
- 【随机化】【并查集】Gym - 100851J - Jump
题意:交互题,有一个长度为n(偶数)的二进制串,你需要猜不超过n+500次猜到它.如果你猜的串与原串相同的位数为n,那么会返回n,如果为n/2,那么会返回n/2,否则都会返回零. 先random,直到 ...
- [转]php-fpm - 启动参数及重要配置详解
约定几个目录/usr/local/php/sbin/php-fpm/usr/local/php/etc/php-fpm.conf/usr/local/php/etc/php.ini 一,php-fpm ...
- python中后端数据序列化不显示中文的解决方法
我们在前后端交互的时候,让序列化的数据更友好的显示,我们会用到 import json js = json.loads('{"name": "多多"}') pr ...
- PHP 基础函数(一)数组的键名和值
array_values($arr); 获取数组的值,键名消失,原数组不变,返回转变后的数组:
- linux下如何启动sybase
isql -Dxxx -Uxxx -P111111 用isql连接数据库发现数据库没有启动. 如何启动sybase数据库? [TA_SYBASE][/home/bta]su - sybase //切 ...