Redux-observable 测试和丢失我的 Marbles

Posted

技术标签:

【中文标题】Redux-observable 测试和丢失我的 Marbles【英文标题】:Redux-observable Testing and losing my Marbles 【发布时间】:2021-12-06 20:25:39 【问题描述】:

我想测试这个 observable 但我什么也没得到:

const fetchUsersApi = (action$: any) => action$.pipe(
    ofType(FETCH_USERS),
    mergeMap(() => from(
        API.graphql(graphqlOperation(queries.LIST_ALL_USERS)),
    ).pipe(
        map((res: any) => fetchUsersSuccess(
            // TODO - standardise this to 'items' so fetchApi can be used
            pathOr(null, ['data', 'listAllUsers', 'users'], res),
        )),
        catchError((error: Error) => 
            const processedError = processAppSyncErrorMessage(error);
            console.log('Error', processedError);
            return of(addError(processedError), fetchUsersError(processedError));
        ),
    )),
);

测试如下:

import  TestScheduler  from 'rxjs/testing';

import  API  from 'aws-amplify';
import  ActionsObservable  from 'redux-observable';
import  fetchUsersSuccess, fetchUsers  from '../actions';

import fetchUsersEpic from './fetchUsersApi';

jest.mock('aws-amplify');

const testScheduler = new TestScheduler(
    (actual, expected) => expect(actual).toStrictEqual(expected),
);

describe('fetchUsersEpic', () => 
    it('should fetch the users', () => 
        const mockFetchedUsers = [
             id: 'fakeUser1' ,
        ];
        (API.graphql as jest.Mock).mockImplementation(() => Promise.resolve(
            data:  listAllUsers:  users: mockFetchedUsers  ,
        ));
        testScheduler.run((helpers) => 
            const 
                hot,
                cold,
                expectObservable,
                flush,
             = helpers;
            const action$ = cold('-a', 
                a: fetchUsers(),
            );

            const reduxObservableaAction = ActionsObservable.from(action$);

            const actual = fetchUsersEpic(reduxObservableaAction);
            const expectedMarbles = '-b';
            const expectedValues =  b: fetchUsersSuccess(mockFetchedUsers) ;

            expectObservable(actual).toBe(expectedMarbles, expectedValues);
            flush();
        );
    );
);

我得到的结果是:

● fetchUsersEpic › 应该获取用户

expect(received).toStrictEqual(expected) // 深度相等

预计 - 18 收到+1

显然我在这里遗漏了一些东西,我认为返回的值应该是带有一些数据的 fetchUsersSuccess() 回调,但我们得到的是一个空数组。能得到一些想法会很棒。

【问题讨论】:

尝试模拟 API.graphql 以返回一个可观察的而不是一个承诺。 Promise 回调是异步执行的 我刚刚决定在商店级别使用黑盒并调度事件,并编写了一个中间件,其中传入了一个 jest.fn() 来接收商店上的任何操作。即使它几乎不是单元测试也能正常工作! 我很高兴你有一些工作。以后请记住,任何时候引入 Promises 都需要等待结果,而不仅仅是大理石测试! 【参考方案1】:

我决定忽略提供的测试解决方案,对整个商店进行测试。它工作得更好,如果我们决定 bin redux-observable,我们仍然可以保留测试。

import 
    applyMiddleware, createStore, Action,
 from 'redux';

import  composeWithDevTools  from 'redux-devtools-extension';
import  createEpicMiddleware, combineEpics, Epic  from 'redux-observable';

import rootReducer from '../../store/reducers';

/**
 * makeActionListMiddleware is a function to allow us to pass a jest mock
 * function to the store to pick up on any actions that are called on it
 */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const makeActionMiddleware = (jestMockFunction: jest.Mock) => (store: object) => (next: any) => (action: Action<any>) => 
    jestMockFunction(action);
    // continue to the next action
    return next(action);
;

// creates a fake version of the store with selected epics combined and a jest
// function middleware that received all store events.
const createEpicTester = (
    epics: Epic[],
    reducers = rootReducer,
    initialState = ,
) => 
    const storeActionJestCallback = jest.fn();
    const jestActionMiddleware = makeActionMiddleware(storeActionJestCallback);
    const composeEnhancers = composeWithDevTools();
    const epicMiddleware = createEpicMiddleware();
    const store = createStore(reducers, initialState, composeEnhancers(
        applyMiddleware(epicMiddleware, jestActionMiddleware),
    ));
    epicMiddleware.run(combineEpics(...epics));
    return  store, dispatch: store.dispatch, storeActionJestCallback ;
;

export default createEpicTester;

用法

const  dispatch, storeActionJestCallback  = createEpicTester([epic]);
dispatch( type: "someEvent" );
expect(storeActionJestCallback).toHaveBeenCalledWith( type: "someEvent" ); 

您现在可以期待您的史诗应用到商店的更多事件。

【讨论】:

以上是关于Redux-observable 测试和丢失我的 Marbles的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 redux-observable 史诗没有被触发?

redux-observable Promise 在单元测试中没有得到解决

React + Redux-Observable + Typescript - 编译,参数不可分配错误

在 catchError 之后不会执行 redux-observable

如何使用 TypeScript 为 Redux-Observable 的 Epic 输入 Actions

在 Axios 中使用 Redux-Observable 的问题