业务复杂多变迭代疾速,加上编写单测其实是消耗肯定工夫去做的,可能很多人认为编写单元测试是一件吃力不讨好的事儿,不会在我的项目中被动的去做单元测试,一两年前笔者也是这样的一种心态,对于单测不屑一顾,然而随着看的书多了,学习的货色多了,明确了单测可有无效的保障咱们一些外围性能的正确性,同样能够反推咱们的设计一些通用性能是否全面,再者也能够在咱们改变一些性能后,校验原有性能的正确性,说这么多,还须要大家本人写起来单测,一个货色好不好,只有用起来了才晓得,在vite下配置jest单测代码一上传至git,有趣味的敌人,能够点此查阅;

(1)、测试项目筹备

  如果咱们通过creat-react-app创立我的项目会间接内置@testing-library/react,能够开箱即用,然而这里咱们通过vite形式创立的react我的项目,vite构建的我的项目,默认是没有单测的,而后一步步欠缺test构建,这样做的益处呢就是咱们本人相熟配置构建流程,脱离cli脚手架工具,自在搭配;

  • 1、应用vite创立一个空白我的项目pnpm create vite react-test-example -- --template react-ts
  • 2、装置react单测相干依赖pnpm add @testing-library/react @testing-library/jest-dom jest -D
  • 3、pnpm jest --init生成jest配置文件

  上面咱们就对于配置我的项目做个阐明

  • 第一:咱们先设置匹配那些文件作为test文件

    testMatch: [  '<rootDir>/src/**/__tests__/**/*.{spec,test}.{js,jsx,ts,tsx}',  '<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}',  '<rootDir>/__test__/**/*.{spec,test}.{js,jsx,ts,tsx}',],
  • 第二步:配置我的项目单测文件类型配置,留神这个配置内容是从左到右执行去查看匹配的,所以如果是ts主导的我的项目,咱们就将ts类型文件放在最后面;

    moduleFileExtensions: [  'ts', 'tsx', 'js', 'jsx' ]
  • 第三步:配置文件文件、门路解析方法,执行这个步骤之前,咱们须要对于文件解析工具进行装置,具体如下:

    • 1、装置pnpm add identity-obj-proxy -D解决css文件
    • 2、对于jest单测文件,因为jest是运行在v8引擎中,也就是会以nodejs环境中来去执行,所以对于文件都要本义操作,惯例有两种形式,第一种ts tsx形式写的单测,咱们能够选中用ts-jest形式疾速解决,第二种形式便是通过babel这种形式配置(繁琐一点),当然说道这儿了,咱们得提一嘴明天我这里找到的另一种形式,那就是基于swc的计划,这套计划劣势(我在理论我的项目亲测)就是执行jest单测文件效率会快过下面两种,速度快的不是一星半点,所以我举荐的用swc这套形式;
    • 3、基于第二步,咱们装置swc单测相干工具内容pnpm add @swc/core @swc/jest -D
    moduleNameMapper: {  '^.+\\.module\\.(css|sass|scss|less)$': 'identity-obj-proxy',  '\\.svg$': 'identity-obj-proxy',  // webpack  or  vite 等编译工具中波及到的别名辨认},transform: {  '^.+\\.(js|jsx|ts|tsx)$': ["@swc/jest"],},transformIgnorePatterns: [  '[/\\\\]node_modules/(?!(antd)/)[/\\\\].+\\.(js|jsx|ts|tsx)$',],
  • 第四步:以上步骤针对于一般测试文件,如果咱们须要对组件相干测试,咱们就须要配置testEnvironment,当初咱们我的项目上应用最新版本的jest后(jest 28 之后的版本),提醒装置的是jest-environment-jsdom,咱们装置后,依照上面配置;

    testEnvironment: 'jsdom',
  • 第五步:下面步骤咱们实现根本版本的jest配置,具体对于coverage等等这些配置能够查看官网,还有setupFiles相干配置依据具体应用状况减少,后续咱们应用@testing-library/react进行,组件测试都须要依赖@testing-library/js-dom所以咱们就间接设置setupFilesAfterEnv,将每个单测都须要的公共内容对立增加,具体如下:

    // ./jest/setupJestDom.tsimport '@testing-library/jest-dom'// 调整jest.config.js, 减少以下内容 setupFilesAfterEnv: [  '<rootDir>/jest/setupJestDom.ts' ],
  • 第六步: 每次执行结束后,不主动清理单测缓存,这样执行效率会快

    setupFiles: [  '@testing-library/react/dont-cleanup-after-each'],

(2)、@testing-library/react的根本用法

  下面咱们做了jest我的项目的配置,基本上能够满足咱们写jest单测了,上面咱们就用应用@testing-library/react形式做一下单元测试的写法,这里我先将@testing-library/reactapi文档地址放在这儿,不便参考应用;
  首先咱们对于惯例组件测试写法,具体如下:

import React from 'react'const HelloWorld: React.FC = () => {  return <div>hello world</div>}export default HelloWorld
单元测试写法
import React from 'react'import { render, screen } from '@testing-library/react'import HelloWorld from '../src/component/HelloWorld'describe('HelloWorld Component', () => {  it('render', () => {    render(<HelloWorld></HelloWorld>)    // 组件渲染后果打印进去,不便咱们去对照    screen.debug()    expect(screen.getByText('hello world')).toBeInTheDocument()  })})

咱们这里总结一下根本用法:

  • render:渲染react组件
  • screen:testing-library/react 提供的全局dom对象,咱们能够通过screen去查dom构造
  • 查找元素:findBy...、getBy...、queryBy

    • 对于getBy... 与 queryBy...两个用法相似,都是去查找页面内容,返回的是fiberNode;
    • 对于findBy...返回的是一个promise,如果页面初始状态没有的元素构造,最终会渲染进去的构造,能够通过这种形式来解决,例如咱们异步申请数据,渲染页面构造,咱们就能够通过findBy办法实现,如下所示:
    import React from 'react'import { render, screen } from '@testing-library/react'import HelloWorld from '../src/component/HelloWorld'describe('HelloWorld Component', () => {it('render', async () => {  render(<HelloWorld></HelloWorld>)  // 是在写单测不分明渲染进去的是啥,能够进行debug  screen.debug()  expect(await screen.findByText('hello world')).toBeInTheDocument()})})
  • 针对jest-dom扩大的匹配器(下面用到的toBeInTheDocument()),还记得咱们通过setupFiles插入进去的工具库@testing-library/jest-dom,外面提供了一系列匹配器,不便咱们去做test,倡议去看官网例子,没必要死记硬背,理论工作要用了就查阅文档;
  • fireEvent,进行事件模仿单元测试,具体用法如下:

    import React, { useState } from 'react'interface HelloWorldProps {onClick: () => void}const HelloWorld: React.FC<HelloWorldProps> = ({ onClick }) => {return <div>  <div>hello world</div>  <button onClick={onClick}>减少</button></div>}export default HelloWorld

      单测文件写法如下:

    import React from 'react'import { render, screen, fireEvent } from '@testing-library/react'import HelloWorld from '../src/component/HelloWorld'describe('HelloWorld Component', () => {it('render', async () => {  const onClick = jest.fn()  render(<HelloWorld onClick={onClick}></HelloWorld>)  // 是在写单测不分明渲染进去的是啥,能够进行debug  screen.debug()  expect(await screen.findByText('hello world')).toBeInTheDocument()  // 模仿事件  fireEvent.click(screen.getByText(/减少/i))  expect(onClick).toHaveBeenCalledTimes(1)})})

    (3)、@testing-library/react-hooks 对于hooks单元测试方法

      针对于hooks的单元测试,咱们能够装置@testing-library/react-hooks工具进行hooks单测,具体装置如下:

  • 首先,咱们须要装置pnpm add @testing-library/react-hooks -D
  • 其次,咱们须要留神@testing-library/react-hooks 不与react版本捆绑在一起,所以如果咱们须要装置react-test-renderer 或者 react-dom两个其中一个,二者都有,会默认以react-test-renderer为第一优先级;


上面对于hooks单测如下:

import { useCallback, useState } from "react"function useCounter() {  const [count, setCount] = useState(0)  const increment = useCallback(() => setCount((x) => x + 1), [])  return { count, increment }}export default useCounter
import { renderHook, act } from '@testing-library/react-hooks'import useCounter from '../src/useCounter'test('Counter', () => {  const { result } = renderHook(() => useCounter())  act(() => {    result.current.increment()  })  expect(result.current.count).toBe(1)})

  单元测试内容分很多,我这里次要就是我的项目构建,应用testing-library进行单测用法简短阐明,喜爱的敌人点赞评论珍藏三连一下,谢谢您们的反对~比心!!