使用 useEffect() hook 在 Jest 中测试一个函数

5

我正在学习 Jest,我需要为 useEffect() 钩子编写一个测试用例,该钩子根据标志 [counter] 进行渲染,并在内部检查字段是否存在 localStorage 值。

function sample(props) {
  const counter = props;
  const [displayIcon, setDisplayIcon] = useState(counter);

  function isLocalstoragePresent() {    
    return localStorage.getItem("some_Id");
  }

  useEffect(() => {
    if (isLocalstoragePresent()) {
      setDisplayIcon(true);
    } else {
      setDisplayIcon(false);
    }
  }, [counter]);

 export default sample;

有人能帮助我编写测试用例/提供关于UseEffect的指导吗?其中还涉及调用isLocalstoragePresent()方法。提前致谢。


Joe Lloyd,感谢您的回复,您能否建议我对上面的代码进行哪些更改? - MK6
isLocalstoragePresent 移至函数外,并进行测试(如果您愿意的话)。然后运行一个浅层测试,其中模拟 isLocalstoragePresent 的返回值分别为正和负。除非您使用 spy 和 mock useState,否则无法获取计数器的值,这需要更复杂的处理。 - Joe Lloyd
好的,谢谢。我会尝试那种方法,这样测试 useEffect() 应该会更简单。 - MK6
是的,这很重要,编写易于测试的代码。这确实简化了您所有的代码。 - Joe Lloyd
我将以下代码分离出来: function isLocalstoragePresent() {
return localStorage.getItem("some_Id"); } 并编写了以下测试用例, it("检查是否调用了isLocalstoragePresent", () => { const isLocalstoragePresent = jest.fn(); expect(isLocalstoragePresent).toHaveBeenCalled(); }); 但仍然失败,我尝试使用mockImplementation而不是jest.fn(),但仍然出现了我提到的错误,请问我做错了什么?请给予建议。
- MK6
显示剩余2条评论
1个回答

5
以下是使用jestjs和react-dom/test-utils进行单元测试的解决方案:
index.tsx:
import React, { useState, useEffect } from 'react';

function sample(props) {
  const { counter } = props;
  const [displayIcon, setDisplayIcon] = useState(counter);

  function isLocalstoragePresent() {
    return localStorage.getItem('some_Id');
  }

  useEffect(() => {
    if (isLocalstoragePresent()) {
      setDisplayIcon(true);
    } else {
      setDisplayIcon(false);
    }
  }, [counter]);

  return <div>{displayIcon ? 'icon' : ''}</div>;
}

export default sample;

index.test.tsx:

import Sample from './';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { act } from 'react-dom/test-utils';

describe('60639673', () => {
  let container;
  beforeEach(() => {
    container = document.createElement('div');
    document.body.appendChild(container);
  });

  afterEach(() => {
    unmountComponentAtNode(container);
    container.remove();
    container = null;
  });

  it('should display icon', async () => {
    jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValueOnce('1');
    const mProps = { counter: false };
    await act(async () => {
      render(<Sample {...mProps}></Sample>, container);
    });
    expect(container.querySelector('div').textContent).toBe('icon');
    expect(localStorage.__proto__.getItem).toBeCalledWith('some_Id');
    localStorage.__proto__.getItem.mockRestore();
  });

  it('should not display icon', async () => {
    jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValueOnce('');
    const mProps = { counter: true };
    await act(async () => {
      render(<Sample {...mProps}></Sample>, container);
    });
    expect(container.querySelector('div').textContent).toBe('');
    expect(localStorage.__proto__.getItem).toBeCalledWith('some_Id');
    localStorage.__proto__.getItem.mockRestore();
  });
});

100%覆盖率的单元测试结果:

 PASS  stackoverflow/60639673/index.test.tsx (9.723s)
  60639673
    ✓ should display icon (41ms)
    ✓ should not display icon (8ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 index.tsx |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        11.242s

依赖项版本:

"react": "^16.12.0",
"react-dom": "^16.12.0",
"jest": "^25.1.0"

源代码: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60639673

该链接提供了一个React和Apollo GraphQL的起始项目,其中包含示例代码和结构。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接