首页 > 解决方案 > Redux-observable 测试和丢失我的 Marbles

问题描述

我想测试这个 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) // 深度相等

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

标签: testingreduxrxjsredux-observablerxjs-marbles

解决方案


我决定忽略提供的测试解决方案,对整个商店进行测试。它工作得更好,如果我们决定 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" }); 

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


推荐阅读