Jest 使用指南 - - tsx 组件篇

Jest 使用指南 - - tsx 组件篇

#jest
首先考虑一下,React 组件我们需要测试哪些东西呢?

  • React 组件的 render 结果是一个组件树,并且整个树最终会被解析成一个纯粹由 HTML 元素构成的树形结构
  • React 组件可以拥有 state,且 state 的变化会影响 render 结果
  • React 组件可以拥有生命周期函数,这些生命周期函数会在特定时间点执行
    这就是我们的测试一个 React 组件需要考虑的东西了

接下来我们来考虑一下怎么去测试,Jest 提供了一个基于快照的测试方式,每当你想要确保你的UI不会有意外的改变,快照测试是非常有用的工具。

这里主要讲一下 React 提供的 react-test-renderreact-dom/test-utils,

  • 如果需要测试事件(如 click, change, blur 等),那么使用 react-dom/test-utils
  • 其它时候使用更简单、灵活的 react-test-renderer

react 组件的单测

import React from "react";
const ShallowRender = () => {
    return (
        <div>
            <p>1111</p>
            <ul>
                <li>2222</li>
                <li>3333</li>
                <li>4444</li>
                <li>5555</li>
            </ul>
        </div>
    );
};
export default ShallowRender;

React-test-render 的 ShallowRenderer

组件只会被 render 一层(children 中的 React 组件不会被 render)
我们可以看一下源码中 getRenderoutput

getRenderOutput() {
    return this._rendered;
}

接着我们再来看看 在 render 方法中对this._rendered 的赋值

if (shouldRender) {
    const prevDispatcher = ReactCurrentDispatcher.current;
    ReactCurrentDispatcher.current = this._dispatcher;
    try {
        // elementType could still be a ForwardRef if it was
        // nested inside Memo.
        if (elementType.$$typeof === ForwardRef) {
            if (!(typeof elementType.render === "function")) {
                throw Error(
                    `forwardRef requires a render function but was given ${typeof elementType.render}.`
                );
            }
            this._rendered = elementType.render.call(
                undefined,
                element.props,
                element.ref
            );
        } else {
            this._rendered = elementType(element.props, this._context);
        }
    } finally {
        ReactCurrentDispatcher.current = prevDispatcher;
    }
}

可以看到 this._rendered 的值是elementType 的返回值,具体 elementType 干嘛可以自己看一下,我们可以看到这里只取了 element 的 type,这个 element就是我们传入的 组件,这里只取了这个组件的 type,并没有深入递归的去取 props 的 children。

import ShallowRenderer from "react-test-renderer/shallow";
import ShallowRender from "./shallowRende";

describe("react component", () => {
    it("shallow render", () => {
        const render = new ShallowRenderer();
        render.render(<ShallowRender />);
        const result = render.getRenderOutput();
        console.log(result.props);
        expect(result.type).toBe("div");
    });
});

React-test-render 的 FullRender

import TestRenderer from "react-test-renderer";
import ShallowRender from "./shallowRende";

describe("full render test", () => {
    it("full test", () => {
        const testRender = TestRenderer.create(<ShallowRender />);
        const testInstance = testRender.root;
        expect(testInstance.findAll((node) => node.type === "li")).toHaveLength(
            4
        );
    });
});

react hooks

对于 hooks 的单测一般使用的库是 @testing-library/react-hooks,它提供了常用的方法有 renderHook , act
renderHook: 渲染一个用于测试的组件,这个组件会调用包含 hook 的callback。
参数说明:
* callback (() => any) - 测试组件每次渲染时调用的方法。这个方法应该调用需要测试的 hook。
* options (object) - 配置对象。有以下配置项:
* initialProps (object) - 传递给callback函数的初始值
* wrapper (componenet) - pass a React Component as the wrapper option to have it rendered around the inner element. This is most useful for creating reusable custom render functions for common data providers
返回值:
* result (object)
* current (any) - callback函数返回的值
* error (Error) - callback函数抛出的错误。
* waitForNextUpdate (function) - 返回一个 Promise,它在下次组件渲染时结束。一般用于由于异步操作导致状态更新的情况。
* rerender (function([newProps])) - 重新渲染测试组件的方法。重新渲染测试组件时,会再次调用callback函数。如果指定了newProps,则会将newProps传递给callback。
* unmount (function()) - 卸载测试组件。一般用于触发useEffect hook 的清除动作。

Act: 帮我们调用一个方法
下面就以常见的 count 变化写一个 hooks

// useCounter.ts
import { useState } from "react";

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

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

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

    return { count, addCount, subCount };
};

export default useCounter;

他的单测试如下这样的

// useCounter.test.ts
import { renderHook, act } from "@testing-library/react-hooks";
import useCounter from "./useCounter";

describe("count hook", () => {
    it("count hook test", () => {
        const { result } = renderHook(() => useCounter());

        act(() => {
            result.current.addCount();
        });
        expect(result.current.count).toBe(1);

        act(() => {
            result.current.subCount();
        });

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

你可能感兴趣的:(前端开发,javascript)