首页 > 解决方案 > 如何在 React 组件中模拟服务以开玩笑地隔离单元测试?

问题描述

我正在尝试重构一个单元测试,以将使用 axios 调用 API 的服务与调用该服务的组件隔离开来。

该服务目前非常简单:

import axios from 'axios'

export default class SomeService {
  getObjects() {
    return axios.get('/api/objects/').then(response => response.data);
  }
}

下面是调用该服务的组件片段:

const someService = new SomeService();

class ObjectList extends Component {
  state = {
    data: [],
  }

  componentDidMount() {
    someService.getObjects().then((result) => {
      this.setState({
        data: result,
      });
    });
  }

  render() {
    // render table rows with object data
  }
}

export default ObjectList

我可以通过模拟 axios 来测试 ObjectList 是否按预期呈现数据:

// ...
jest.mock('axios')

const object_data = {
  data: require('./test_json/object_list_response.json'),
};

describe('ObjectList', () => {
  test('generates table rows from object api data', async () => {

    axios.get.mockImplementationOnce(() => Promise.resolve(object_data));

    const { getAllByRole } = render(
      <MemoryRouter>
        <table><tbody><ObjectList /></tbody></table>
      </MemoryRouter>
    );

    await wait();

    // test table contents

  });
});

一切顺利通过。作为一项主要的学术练习,我试图弄清楚如何模拟 SomeService 而不是 axios,这就是事情出错的地方,因为我认为我对传递的内部结构了解不够。

例如,我认为由于 SomeService 只是返回 axios 响应,我可以类似地模拟 SomeService,有点像这样:

// ...
const someService = new SomeService();

jest.mock('./SomeService')

const object_data = {
  data: require('./test_json/object_list_response.json'),
};

describe('ObjectList', () => {
  test('generates table rows from object api data', async () => {

    someService.getObjects.mockImplementationOnce(() => Promise.resolve(object_data))

// etc.

这失败并出现错误:Error: Uncaught [TypeError: Cannot read property 'then' of undefined],并且错误从以下位置追溯到此行ObjectList

someService.getObjects().then((result) => {

我具体需要模拟ObjectList什么,以便组件可以获取SomeService设置状态所需的内容?

标签: javascriptreactjsunit-testingjestjs

解决方案


在使用 jest 文档中建议的不同方法进行了一些试验和错误之后,似乎唯一可行的方法是jest.mock()使用模块工厂参数调用,如下所示:

// rename data to start with 'mock' so that the factory can use it
const mock_data = {
  data: require('./test_json/object_list_response.json'),
};

jest.mock('./SomeService', () => {
  return jest.fn().mockImplementation(() => {
    return {
      getObjects: () => {
        return Promise.resolve(mock_data).then(response => response.data)
      }
    };
  });
});

// write tests

使用mockResolvedValue()不起作用,因为我无法.then()摆脱它。

如果这会导致任何人找到更优雅或惯用的解决方案,我会欢迎其他答案。


推荐阅读