首页 > 解决方案 > 如何在异步等待操作中测试 catch 语句

问题描述

问题

我有一个等待 API 函数的操作。使用我的模拟 API 可以轻松测试 try 中的快乐路径。但是,不确定测试和覆盖.catch.

行动

import {getRoles} from '../shared/services/api';

export const Actions = {
    SET_ROLES: 'SET_ROLES'
};

export const fetchRoles = () => async dispatch => {
    try {
        const response = await getRoles();
        const roles = response.data;

        dispatch({
            type: Actions.SET_ROLES,
            roles
        });
    } catch (error) {
        dispatch({
            type: Actions.SET_ROLES,
            roles: []
        });
    }
};

动作测试

import {fetchRoles} from '../party-actions';
import rolesJson from '../../shared/services/__mocks__/roles.json';
jest.mock('../../shared/services/api');

describe('Roles Actions', () => {
    it('should set roles when getRoles() res returns', async () => {
        const mockDispatch = jest.fn();

        await fetchRoles()(mockDispatch);
        try {
            expect(mockDispatch).toHaveBeenCalledWith({
                type: 'SET_ROLES',
                roles: rolesJson
            });
        } catch (e) {
            // console.log('fetchRoles error: ', e)
        }
    });

    // Here is the problem test, how do we intentionally cause
    // getRoles() inside of fetchRoles() to throw an error?
    it('should return empty roles if error', async () => {
        const mockDispatch = jest.fn();

        await fetchRoles('throwError')(mockDispatch);

        expect(mockDispatch).toHaveBeenCalledWith({
            type: 'SET_ROLES',
            roles: []
        });
    });
});

模拟 API

import rolesJson from './roles.json';

export const getRoles = async test => {
    let mockGetRoles;

    if (test === 'throwError') {
        //   console.log('sad')
        mockGetRoles = () => {
            return Promise.reject({
                roles: []
            });
        };
    } else {
        // console.log('happy')
        mockGetRoles = () => {
            return Promise.resolve({
                roles: rolesJson
            });
        };
    }

    try {
        const roles = mockGetRoles();
        // console.log('api mocks roles', roles);
        return roles;
    } catch (err) {
        return 'the error';
    }
};

^ 上面你可以看到我尝试过的,确实有效,但它要求我以适合测试的方式更改我的代码,而不是应用程序的实际逻辑。

例如,要通过这个测试,我必须通过实际代码传入一个变量(请参阅 参考资料x):

export const fetchRoles = (x) => async dispatch => {
    try {
        const response = await getRoles(x);
        const roles = response.data;

我们如何getRoles在我们的模拟中强制在我们的悲伤路径中抛出一个错误,.catch测试?

标签: javascriptunit-testingreact-reduxjestjs

解决方案


您可以在每个测试的基础上模拟getRolesAPI:

// getRoles will be just jest.fn() stub
import {getRoles} from '../../shared/services/api'; 
import rolesJson from '../../shared/services/__mocks__/roles.json';

// without __mocks__/api.js it will mock each exported function as jest.fn();
jest.mock('../../shared/services/api'); 

it('sets something if loaded successfully', async ()=> {
  getRoles.mockReturnValue(Promise.resolve(rolesJson));
  dispatch(fetchRoles());
  await Promise.resolve(); // so mocked API Promise could resolve
  expect(someSelector(store)).toEqual(...);
});

it('sets something else on error', async () => {
  getRoles.mockReturnValue(Promise.reject(someErrorObject));
  dispatch(fetchRoles());
  await Promise.resolve();
  expect(someSelector(store)).toEqual(someErrornessState);
})

我还建议您在调用后专注于存储状态,而不是调度的操作列表。为什么?因为实际上我们并不关心在存储预期数据时以什么顺序发送了哪些操作,对吧?

但可以肯定的是,您仍然可以断言反对dispatch电话。要点:不要模拟在自动模拟中返回的结果,__mocks__而是在对等基础上进行。


推荐阅读