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的更多相关文章

  1. React Hook:使用 useEffect

    React Hook:使用 useEffect 一.描述 二.需要清理的副作用 1.在 class 组件中 2.使用 effect Hook 的示例 1.useEffect 做了什么? 2.为什么在组 ...

  2. React Hooks --- useState 和 useEffect

    首先要说的一点是React Hooks 都是函数,使用React Hooks,就是调用函数,只不过不同的Hooks(函数)有不同的功能而已.其次,React Hooks只能在函数组件中使用,函数组件也 ...

  3. React中useLayoutEffect和useEffect的区别

    重点: 1.二者函数签名相同,调用方式是一致的 2. 怎么简单进行选择: 无脑选择useEffect,除非运行效果和你预期的不一致再试试useLayoutEffect 区别详解:useEffect是异 ...

  4. React Native Android原生模块开发实战|教程|心得|怎样创建React Native Android原生模块

    尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://blog.csdn.net/fengyuzhengfan/article/details/54691503) 告诉大家一个好消息. ...

  5. 如何使用TDD和React Testing Library构建健壮的React应用程序

    如何使用TDD和React Testing Library构建健壮的React应用程序 当我开始学习React时,我努力的一件事就是以一种既有用又直观的方式来测试我的web应用程序. 每次我想测试它时 ...

  6. [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 ...

  7. react创建项目后运行npm run eject命令将配置文件暴露出来时报错解决方法

    最近在用create-react-app创建项目,因要配置各种组件,比如babel,antd等, 需要运行npm run eject命令把项目的配置文件暴露出来,但是还是一如既然碰到报错,因为是在本地 ...

  8. react+ant design 项目执行yarn run eject 命令后无法启动项目

    如何将内建配置全部暴露? 使用create-react-app结合antd搭建的项目中,项目目录没有该项目所有的内建配置, 1.执行yarn run eject 执行该命令后,运行项目yarn sta ...

  9. React 16 源码瞎几把解读 【二】 react组件的解析过程

    一.一个真正的react组件编译后长啥样? 我们瞎几把解读了react 虚拟dom对象是怎么生成的,生成了一个什么样的解构.一个react组件不光由若干个这些嵌套的虚拟dom对象组成,还包括各种生命周 ...

随机推荐

  1. CPPUNIT_TEST

    (1) CPPUNIT_ASSERT(condition):判断condition的值是否为真,如果为假则生成错误信息. (2)CPPUNIT_ASSERT_MESSAGE(message, cond ...

  2. FastReport.Net使用:[15]富文本控件使用

    富文本(Rich Text)控件用于显示Rtf格式的文本. 认识富文本编辑窗体 1.下图就是富文本的编辑窗体,乍一看就像Word一样,不过功能没有Word强大了.具体功能就不一一介绍了,用个Word的 ...

  3. 【BZOJ 4555】 4555: [Tjoi2016&Heoi2016]求和 (NTT)

    4555: [Tjoi2016&Heoi2016]求和 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 315  Solved: 252 Des ...

  4. 【BZOJ 4170】 4170: 极光 (CDQ分治)

    4170: 极光 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 121  Solved: 64 Description "若是万一琪露诺(俗 ...

  5. android bitmap recycle

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 不需要位图的时候,就可以手动调用recycle. 2.3之前 位图对象 存在 java的 ...

  6. manacher算法求最长回文子序列

    一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...

  7. [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status ...

  8. MySQL启动项提权

    关于MySQL的启动项提权,听其名知其意.就是将一段 VBS脚本导入到  C:\Documents and Settings\All Users\「开始」菜单\程序\启动 下,如果管理员重启了服务器, ...

  9. App图标生成器的实现,附源码

    背景 网上各种生成app图标的网站和工具,感觉还是用自己写的心里畅快点,所以花了1个小时自己写了个.按照网上给的图标尺寸规范把所有尺寸的都生成了. 建了一个讨论群,533838427.很明显是针对独立 ...

  10. Arab Collegiate Programming Contest 2012 J- Math Homework

    思路:由于只有1-6这几个数,而这几个数的最小公倍数是60,所以只需要求出60以内有多少满足条件的数即可. 再就是求出对于给定的n,求出60的倍数.然后就是怎样求的问题了. 首先可以写成如下形式:   ...