如何使用 jest 和 enzyme 测试 redux saga

How to test redux saga using jest and enzyme

我有一个 saga 函数,我正在尝试使用 jest 和 enzyme 进行测试,但出现错误。这是我第一次测试 saga 文件,所以我不知道为什么我会收到错误,即使预期值和接收值是相同的 saga 文件如下:

export const getToken = (state) => state.dextaAuth.token;

export function* setPost(action) {
  try {
    const token = yield select(getToken);
    const data = yield call(
      dextaApiService.post,
      "/api/savedAdvices",
      action.payload,
      token
    );
    yield put(
      openNotification({
        messageType: MessageBarType.success,
        message: "Advice saved successfully",
      })
    );
  } catch (err) {
    yield put(
      openNotification({
        messageType: MessageBarType.error,
        message: `Notification: ${err.message}`,
      })
    );
  }
}

export function* savedAdvicesaga() {
  yield takeLatest(POST_SAVED_ADVICE, setPost);
  yield takeLatest(FETCH_SAVED_ADVICE, fetchSavedAsviceAsync);
}

这是我写的测试用例:

describe("savedAdviceSaga", () => {
  const genObject = savedAdvicesaga();
  describe("setpost", () => {
    it("should check for post ", async () => {
      const apiResponse = jest
        .spyOn(dextaApiService, "post")
        .mockImplementation(() => Promise.resolve());

      const dispatched = [];
      const action: types.SavedAdviceActionType = {
        type: types.POST_SAVED_ADVICE,
        payload: {
          Title: "test",
          Description: "test dec",
        },
      };
      const iterator = setPost(action);
      const effect = iterator.next().value;
      const expected = select(getToken);
      const result = await runSaga(
        {
          dispatch: (action) => dispatched.push(action),
        },
        setPost,
        action
      );
      expect(effect).toContainEqual(expected);
      expect(apiResponse).toHaveBeenCalledTimes(1);
    });
  });
});

这是我在控制台中遇到的错误:

expect(received).toContainEqual(expected) // deep equality

Expected value:  {"@@redux-saga/IO": true, "combinator": false, "payload": {"args": [], 
    "selector": [Function anonymous]}, "type": "SELECT"}
Received object: {"@@redux-saga/IO": true, "combinator": false, "payload": {"args": [], "selector": 
     [Function anonymous]}, "type": "SELECT"}

   27 |         dispatch: (action) => dispatched.push(action),
   28 |       }, setPost, action);
 > 29 |       expect(effect).toContainEqual(expected)
      |                      ^
   30 |       expect(apiResponse).toHaveBeenCalledTimes(1);
   31 |
   32 |     })

您正在使用 Testing the full Saga 测试策略。不需要检查 saga 的每一步。

run the whole saga and assert that the expected effects have occurred.

runSaga(options, saga, ...args) 接受 getState() 选项,以便您提供模拟状态。 getToken 选择器将使用它。

例如

saga.ts:

import { call, put, select, takeLatest } from 'redux-saga/effects';
import { dextaApiService } from './dextaApiService';

export const MessageBarType = {
  success: 'success',
  error: 'error',
};

export function openNotification({ messageType, message }) {
  return { type: messageType, payload: message };
}

export const getToken = (state) => state.dextaAuth.token;

export function* setPost(action) {
  try {
    const token = yield select(getToken);
    const data = yield call(dextaApiService.post, '/api/savedAdvices', action.payload, token);
    yield put(
      openNotification({
        messageType: MessageBarType.success,
        message: 'Advice saved successfully',
      }),
    );
  } catch (err) {
    yield put(
      openNotification({
        messageType: MessageBarType.error,
        message: `Notification: ${err.message}`,
      }),
    );
  }
}

export function* savedAdvicesaga() {
  yield takeLatest('POST_SAVED_ADVICE', setPost);
}

dextaApiService.ts:

export const dextaApiService = {
  async post(path, payload, token) {
    return 'real implementation';
  },
};

saga.test.ts:

import { setPost } from './saga';
import { dextaApiService } from './dextaApiService';
import { runSaga } from 'redux-saga';
import { Action } from 'redux';

describe('67444295', () => {
  it('should pass', async () => {
    const dispatched: Action[] = [];
    const postSpy = jest.spyOn(dextaApiService, 'post').mockResolvedValueOnce('mocked response');
    const actual = await runSaga(
      {
        dispatch: (action: Action) => dispatched.push(action),
        getState: () => ({ dextaAuth: { token: 'abc123' } }),
      },
      setPost,
      { payload: 'mocked payload' },
    ).toPromise();
    expect(postSpy).toBeCalledWith('/api/savedAdvices', 'mocked payload', 'abc123');
    expect(dispatched).toEqual([{ type: 'success', payload: 'Advice saved successfully' }]);
    postSpy.mockRestore();
  });
});

测试结果:

 PASS  src/Whosebug/67444295/saga.test.ts
  67444295
    ✓ should pass (5 ms)

--------------------|---------|----------|---------|---------|-------------------
File                | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------------|---------|----------|---------|---------|-------------------
All files           |   85.71 |        0 |      60 |      80 |                   
 dextaApiService.ts |      50 |      100 |       0 |      50 | 3                 
 saga.ts            |   89.47 |        0 |      75 |   84.62 | 26,36             
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.567 s