简体   繁体   English

如何在 async await Action 中测试 catch 语句

[英]How to test catch statement in async await Action

Problem问题

I have an Action which awaits an API function.我有一个等待 API 函数的操作。 The happy path in the try is easily testable with my mocked API.使用我的模拟 API 可以轻松测试 try 中的快乐路径。 However, unsure as to the best way to test and cover the .catch .但是,不确定测试和覆盖.catch的最佳方法。

Actions行动

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: []
        });
    }
};

Actions Test动作测试

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: []
        });
    });
});

Mocked API模拟 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';
    }
};

^ Above you can see what I tried, which did work, but it required me to change my code in a way that fit the test, but not the actual logic of the app. ^ 以上您可以看到我尝试过的方法,它确实有效,但它要求我以适合测试的方式更改我的代码,而不是应用程序的实际逻辑。

For instance, for this test to pass, I have to pass in a variable through the real code (see x ):例如,要通过此测试,我必须通过实际代码传入一个变量(请参阅x ):

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

How can we force getRoles in our mock to throw an error in our sad path, .catch test?我们如何强制模拟中的getRoles在我们的悲伤路径.catch测试中抛出错误?

You can mock getRoles API on per-test basis instead:您可以在每个测试的基础上模拟getRoles API:

// 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);
})

I also propose you concentrate on store state after a call not a list of actions dispatched.我还建议您在调用后专注于存储状态,而不是调度的操作列表。 Why?为什么? Because actually we don't care what actions in what order has been dispatched while we get store with data expected, right?因为实际上我们并不关心在我们存储预期数据时按什么顺序分派了哪些操作,对吗?

But sure, you still could assert against dispatch calls.但是可以肯定的是,您仍然可以对dispatch调用进行断言。 The main point: don't mock result returned in __mocks__ automocks but do that on peer-basis.要点:不要模拟__mocks__ automocks 中返回的结果,而是在对等基础上这样做。

I resolved the test and got the line coverage for the .catch by adding a function called mockGetRolesError in the mock api file:我通过在模拟 api 文件中添加一个名为mockGetRolesError的函数解决了测试并获得了.catch的行覆盖:

Thanks to @skyboyer for the idea to have a method on the mocked file.感谢@skyboyer 提出在模拟文件上使用方法的想法。

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;
        // console.log('ACTION roles:', roles);

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

Now in the test for the sad path, I just have to call mockGetRolesError to set the internal state of the mocked api to be in a return error mode.现在在对悲伤路径的测试中,我只需要调用mockGetRolesErrormockGetRolesError的 api 的内部状态设置为返回错误模式。

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

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

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

    it('should return empty roles if error', async () => {
        const mockDispatch = jest.fn();

        mockGetRolesError();
        await fetchRoles()(mockDispatch);

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM