2

React 搭配Testing Library 实作测试

 2 months ago
source link: https://www.fly63.com/article/detial/12674
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

更新日期: 2024-03-01阅读: 190标签: 测试分享

扫一扫分享

本篇使用的JavaScript 框架Jest,所以在开始前要先了解一些Jest 基本语法,才会比较快理解喔!

虽然Jest 本身有提供很多测试方法,但在测试上都比较偏向逻辑测试,像是a + b 是否等于c。而实际上我们所需要的测试有一大部分也包含UI 的测试,像是画面上有没显示正确文字,或是使用者点击有没有跳出视窗等等,这时候就是Testing Library出马的时候了!

它的官网介绍就很明确地告诉你

The @testing-library family of packages helps you test UI components in a user-centric way.

也就是@testing-library 系列的套件可以帮助你以用户为中心的方式测试UI 元件。

Testing Library 不只提供可以共通使用的测试套件,还有专为各个框架所出的套件,基本的三大框架reactvueangular 都有属于他们的测试UI 套件。而比较新的框架像是Preact、Svelte 也都有在持续更新。

Testing Libaray For React

以React 来说,搭配Jest 会需要的Testing Libary 有以下几个:

1. @testing-library/react - 模拟React 渲染,Ex:render、screen

2. @testing-library/jest-dom - 扩充jest 的断言库,Ex:toBeInTheDocument、toHaveClass

3. @testing-library/user-event - 模拟使用者操作,Ex:useEvent.click、userEvent.type

接下来来各别介绍这三个的使用时机:

◆ @testing-library/react

主要是提供模拟渲染UI 画面的函式,比较常用的有

1.render()

就如同React 中的render,就是模拟渲染React 元件,假设有一个<Home/>元件:

export const Home = () => {
  return <h1>Home Page</h1>
};

要测试元件的DOM 元素时,就可以使用render()函式。

import { render } from '@testing-library/react'

describe('testing home component', () => {
  it('show Home Page in the home component', () => {
    const {getByText} = render(<Home/>);
    expect(getByText('Home Page')).toBeTruthy(); //使用 getByText 判斷抓到文字的元素是否存在
  });
});

render() 函式会回传一个物件包含一些属性对象:

  • Query:getBy、queryBy、findBy、getAllBy、queryAllBy、findAllBy,取得元件内的元素。

  • container:渲染的DOM 节点。

  • debug:侦错函式,可以显示当前的DOM 结构。

  • rerender:重新渲染元件。

  • unmount:取消渲染。

Query 有很多不同的取元素方法,有get、find、query,各自都有取得多元素的all 方法

Query 种类\结果

没有符合

一项符合大于一个符合是否为非同步函式
getBy...Throw errorThrow error
queryBy...回传nullThrow error
findBy...Throw errorThrow error
getAllBy...Throw error

回传阵列元素

回传阵列元素
queryAllBy...回传[]

回传阵列元素

回传阵列元素
findAllBy...Throw error

回传阵列元素

回传阵列元素

看起来很容易搞混,不过他们都有各自使用的时机

  • getBy...:大部分都可以使用,判断元素是否存在。

  • queryBy...:因为找不到会回传null的特性,所以常用来判断元素是否一开始不存在。

  • findBy...:非同步函式,可以判断需等待的元素是否存在,例如api 回传才会显示在画面上。

2.screen()

虽然render()解决了模拟UI 的情况,不过只能根据render()的内容进行测试,如果有多个render()函式测起来就会比较麻烦,这时候就可以使用screen()。

其实screen()算是@testing-library/dom 所提供,而所有框架的@testing-library 底层都有@testing-library/dom,所以这边才可以直接做使用。

而screen()所抓取的元素是<body></body> 内的所有DOM 元素。

import { render, screen } from '@testing-library/react'

describe('testing home component', () => {
  it('show Home Page in the home component', () => {
    render(<Home/>);
    render(<Home/>);
    expect(getAllByText('Home Page')).toBeTruthy(); //使用 getAllByText 一次判斷兩個 Home 元件元素是否存在
  });
});

个人在抓元素时是比较常用  screen()胜过于使用render()回传的方法,使用起来也比较方便。

3.renderHook()

顾名思义就是去模拟客制化的React Hook,假设今天有一个计数器的customhook

import { useState } from 'react';

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return { count, increment, decrement };
}

export default useCounter;

要测试这个hook 就可以使用renderHook(),他可以传入两个参数,第一个就是要测试的函式,第二是传入函式的参数(非必要)。

import useCounter from './useCounter';
import { renderHook, act } from '@testing-library/react';

it("should increment and decrement the counter", () => {
  const { result } = renderHook(useCounter);

  expect(result.current.count).toBe(0);

  act(() => {
    result.current.increment(); // 呼叫 + 1 函式
  });

  expect(result.current.count).toBe(1);

  act(() => {
    result.current.decrement(); // 呼叫 -1 函式
  });

  expect(result.current.count).toBe(0);
});

renderHook()会回传一个物件,可以使用result属性利用 result.current去操控函式的回传物件。

如果有更改state的操作,就需要使用act包起来,这样才可以即时更新state的状态。

◆ @testing-library/jest-dom

@testing-library/jest-dom 提供给Jest 很多DOM 元素的扩充判断,让我们在抓元素时,有更多的方法去测试,比较常用的像是toBeInTheDocument、toHaveClass等等。

以刚刚的<Home/>渲染测试为例

import { render } from '@testing-library/react'

describe('testing home component', () => {
  it('show Home Page in the home component', () => {
    const {getByText} = render(<Home/>);
    expect(getByText('Home Page')).toBeInTheDocument(); //判斷元素是否存在於 Document
  });
});
import { render } from '@testing-library/react'

describe('testing div', () => {
  it('test div classname is hide', () => {
    render(<div className='hide'>test</div>);
    expect(screen.getByText('test')).toHaveClass('hide'); //判斷元素是否含有指定的 class name
  });
});

◆ @testing-library/user-event

最后一个就是模拟使用着操作,user-event 常常跟另一个@testing-library/react 的fireEvent 拿来比较,fireEvent 就是在程式码中会用的事件处理,像是click 或是change,而user-event的底层就是fireEvent,不过userEvent 能更贴合使用者的模拟情况,像是同样是在input 输入文字,如果使用fireEvent 就会使用change 的事件。

fireEvent:

import { fireEvent } from '@testing-library/react';

fireEvent.change(inputElement, { target: { value: 'Hello, world!' } });

不过如果是用userEvent 就会使用type 的事件。

userEvent:

import userEvent from '@testing-library/user-event';

const user = userEvent.setup()
user.type(inputElement, 'Hello, world!');

乍看之下没有什么不一样,不过userEvent 的type 还包含使用者点击input,输入文字的keydown、keyup 事件,比起fireEvent 的change,更贴近实际的使用者操作情况。

除此之外,userEvent 还有提供很多实用的方法像是:

  • dblClick:点击两次

  • tripleClick:点击三次

  • type:input 输入

  • upload:上传

  • hover/unhover:指标移进移出

  • copy/paste:复制/贴上

详细的可以参考User Interactions

链接: https://www.fly63.com/article/detial/12674


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK