为啥我不能在这里使用 mockResolvedValue?

Posted

技术标签:

【中文标题】为啥我不能在这里使用 mockResolvedValue?【英文标题】:Why am I unable to use mockResolvedValue here?为什么我不能在这里使用 mockResolvedValue? 【发布时间】:2021-01-06 22:10:42 【问题描述】:

我不清楚在使用以下设置时如何模拟我的 api 响应。

我有以下测试:

import React from 'react';
import  cleanup, render, wait  from '@testing-library/react';
import axios from 'axios';
import Condition from './index.jsx';

jest.mock('axios', () => 
  return 
    create: jest.fn(() => (
      get: jest.fn().mockResolvedValue( data:  ),
      interceptors: 
        request:  use: jest.fn(), eject: jest.fn() ,
        response:  use: jest.fn(), eject: jest.fn() ,
      ,
    )),
  ;
);


afterEach(cleanup);

test('fetches and displays data', async () => 
  axios.get.mockResolvedValue( data: 'mock data' );
  const  container  = render(<Condition ...props />);
  await wait(() => expect(container.textContent).toContain('Current milestone'));
  expect(container).toBeDefined();
);

... 以及以下 api 助手:

import axios from 'axios';

const api = axios.create(
  baseURL: window.apiPath,
  withCredentials: true,
);

api.interceptors.request.use(config => 
  const newConfig = Object.assign(, config);
  newConfig.headers.Accept = 'application/json';

  return newConfig;
, error => Promise.reject(error));

export default api;

我的Condition 组件在挂载时使用以下函数来获取数据:

const fetchAsync = async endpoint => 
  const result = await api.get(endpoint);
  setSuffixOptions(result.data.data);
  console.log('result.data.data', result.data.data);
;

测试中的axios.get.mockResolvedValue( data: 'mock data' );行导致如下错误:

    TypeError: Cannot read property 'mockResolvedValue' of undefined

      124 |
      125 | test('fetches and displays data', async () => 
    > 126 |   axios.get.mockResolvedValue( data: 'mock data' );
          |             ^
      127 |   const  container  = render(<Condition ...props />);
      128 |   await wait(() => expect(container.textContent).toContain('Current milestone'));
      129 |   expect(container).toBeDefined();

我应该使用不同的方法来模拟响应吗?

编辑 调用axios.create.get.mockResolvedValue( data: 'mock data' ); 会导致同样的错误。

【问题讨论】:

因为你的 Axios 测试替身只有一个 create prop,没有 get。 但是create方法是创建api的,它在api文件中有get和interceptors方法。如何为该形状修改axios.get.mockResolvedValue 当然,但是您在测试中没有调用它 调用 axios.create.get.mockResolvedValue( data: 'mock data' ); 会导致同样的错误。 axios.create.get 正在尝试访问 get 属性 on 函数,它不会调用函数。 【参考方案1】:

要模拟您的 api 响应,您可以将 jest.spyOnmockImplementation 结合使用,例如如下:

import api from './api';

const mock = jest.spyOn(api,"get");
mock.mockImplementation(() => Promise.resolve( data:  ));

在上面的示例中,api.get 方法被替换为模拟返回 data: 'mock data' 的成功调用。

它可以放在你的test函数中,如下:

test('fetches and displays data', async () => 
    const mock = jest.spyOn(api, "get");
    mock.mockImplementation(() => Promise.resolve( data:  ));
    const  container  = render(<Condition ...props />);
    await wait(() => expect(container.textContent).toContain('Current milestone'));
    expect(container).toBeDefined();
);

【讨论】:

我会将响应数据传递到哪里? 在 Promise.resolve 中,像这样:mock.mockImplementation(() =&gt; Promise.resolve( data: )) 介意在答案中放弃吗?那我就接受了。【参考方案2】:

更好的方法是模拟 ./api 辅助模块而不是 axios 模块。因为您正在测试导入 ./api 作为其直接依赖项的组件。对于组件,axios 模块是间接依赖。如果你想测试./api helper,那么你应该模拟axios 模块,因为现在它成为./api 的直接依赖。

例如

index.tsx:

import React,  useEffect, useState  from 'react';
import api from './api';

function Condition(props) 
  const [options, setSuffixOptions] = useState();
  const fetchAsync = async (endpoint) => 
    const result = await api.get(endpoint);
    setSuffixOptions(result.data);
    console.log('result.data', result.data);
  ;
  useEffect(() => 
    fetchAsync('http://localhost:3000/api');
  );

  return <div>options</div>;


export default Condition;

index.test.tsx:

import React from 'react';
import Condition from './';
import  render, wait  from '@testing-library/react';
import api from './api';
import  mocked  from 'ts-jest/utils';

jest.mock('./api', () => 
  return  get: jest.fn() ;
);

describe('63981693', () => 
  test('fetches and displays data', async () => 
    mocked(api.get).mockResolvedValue( data: 'mock data' );
    const props = ;
    const  container  = render(<Condition ...props />);
    await wait(() => expect(container.textContent).toContain('mock data'));
    expect(api.get).toBeCalledWith('http://localhost:3000/api');
    expect(container).toBeDefined();
  );
);

我们不关心./api 模块的实现,因为我们已经模拟了它。

带有覆盖率报告的单元测试结果:

 PASS  src/***/63981693/index.test.tsx (12.377s)
  63981693
    ✓ fetches and displays data (68ms)

  console.log src/***/63981693/index.tsx:1461
    result.data mock data

  console.log src/***/63981693/index.tsx:1461
    result.data mock data

-----------|----------|----------|----------|----------|-------------------|
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:       1 passed, 1 total
Snapshots:   0 total
Time:        14.873s, estimated 18s

【讨论】:

你在这里使用ts-jest,但项目不是用打字稿写的。

以上是关于为啥我不能在这里使用 mockResolvedValue?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能成功使用 composer require packageName?

为啥 PanResponder 不能使用 useEffect Hook?

为啥使用 Vue 编辑数据不能按预期工作

为啥我不能直接使用 req.body 作为参数?

为啥我不能与背面的链接进行交互?

为啥我不能将异步代码作为同步运行 [重复]