jfairbank / redux-saga-test-plan

Test Redux Saga with an easy plan.
http://redux-saga-test-plan.jeremyfairbank.com
MIT License
1.25k stars 127 forks source link

Error message when expected action not found in actual list. #345

Open MNF opened 4 years ago

MNF commented 4 years ago

We have a saga, that put 2 actions and we wanted to test, that one of the actions was put correctly. We’ve got an error:

put expectation unmet:       Expected     --------     { channel: null,       action:        { type: 'STORE_UPDATED_AIRPORTS',          payload:           { updatedAirports:              { newArrivalAirport: [Object] } } } }       Actual:     ------     1. { channel: null,       action:        { type: 'STORE_UPDATED_AIRPORTS',          payload:           { updatedAirports:              { newArrivalAirport: [Object] } } } }     2. { channel: null,       action:        { type: 'SAVE_CHANGED_DATE_SEGMENTS',          payload: { updatedSegmentData: [ [Object] ] } } }

  1. We are new for redux-saga-test-plan and after reading the message decided that we need to add second action to the expected results. It took a while to understand that library tries to find matched element in the list of saved actions. Could you please in case if the actual has more than one action, show the message like

put expectation not found in the list of actual actions.

  1. It will be good to have a VERBOSE option to show deep serialization of the object, because newArrivalAirport: [Object] in expected and actual were different, but it was hard to guess. Similar comment on https://github.com/jfairbank/redux-saga-test-plan/issues/262#issuecomment-497654515 "in this case payload.action.payload.error.message don't match, but you cannot figure it out from the message because the object serialization does not reach the necessary depth." It would also resolve the https://github.com/jfairbank/redux-saga-test-plan/issues/291

  2. For my example only one of actuals had the same action type as expected ( I believe it is the most common scenario). In this case ideally I want to see error message like

We found actual with the same type as expected, but particular property Action.payload.updatedAirports.newArrivalAirport doesn't match expected property

The different ways to implement deep comparison are described in https://stackoverflow.com/questions/8572826/generic-deep-diff-between-two-objects

molteninjabob commented 4 years ago

I'm in the process of evaluating redux-saga-test-plan right now to see if it's something we want to adopt. In regards to your second point, you can add .then( (result) => {}); to look at the results of all effects. Putting console.log(result)in the curly braces will give you something like this:

{ storeState: undefined, returnValue: undefined, effects: { call: [ [Object] ] }, allEffects: [ { '@@redux-saga/IO': true, combinator: false, type: 'PUT', payload: [Object] }, { '@@redux-saga/IO': true, combinator: false, type: 'CALL', payload: [Object] }, { '@@redux-saga/IO': true, combinator: false, type: 'PUT', payload: [Object] }, { '@@redux-saga/IO': true, combinator: false, type: 'PUT', payload: [Object] } ], toJSON: [Function: toJSON] }

If you then want to go deeper, you can iterate through allEffects to see the actual data. For example:

.then((result) => { console.log(result.allEffects[0]); console.log(result.allEffects[1]); console.log(result.allEffects[2]); console.log(result.allEffects[3]); })

would result in:

{ '@@redux-saga/IO': true, combinator: false, type: 'PUT', payload: { channel: undefined, action: { type: 'SET_LOADING', payload: 'fetch-location-details' } } }

console.log src/redux/postman/tests/mockserver.test.js:101 { '@@redux-saga/IO': true, combinator: false, type: 'CALL', payload: { context: null, fn: [Function: fetchDetails], args: [ '96bb3af1-xxxx-xxxx-xxxx-73f0xxxxxxxx' ] } }

console.log src/redux/postman/tests/mockserver.test.js:102 { '@@redux-saga/IO': true, combinator: false, type: 'PUT', payload: { channel: undefined, action: { type: 'UPDATE_LOCATION', payload: [Object] } } }

console.log src/redux/postman/tests/mockserver.test.js:103 { '@@redux-saga/IO': true, combinator: false, type: 'PUT', payload: { channel: undefined, action: { type: 'UNSET_LOADING', payload: 'fetch-location-details' } } }

You could continue to drill down into the objects and, I presume, do actual assertions on the data of each effect, if it's not already covered by expectSaga.

Hope that helps.

michael-freidgeim-webjet commented 4 years ago

@molteninjabob , thanks, I will try

.then((result) => { console.dir(result, { depth: null}); }) as suggested at https://stackoverflow.com/questions/10729276/how-can-i-get-the-full-object-in-node-jss-console-log-rather-than-object/27534731#27534731 and play with different dir/inspect options.

leo-diehl commented 4 years ago

I have the same problem here, my exposed "effects" object is being returned as empty, even with the "allEffects" being populated

{
  storeState: undefined,
  returnValue: undefined,
  effects: {},
  allEffects: [
    {
      '@@redux-saga/IO': true,
      combinator: false,
      type: 'CALL',
      payload: [Object]
    },
    {
      '@@redux-saga/IO': true,
      combinator: false,
      type: 'CALL',
      payload: [Object]
    },
    {
      '@@redux-saga/IO': true,
      combinator: false,
      type: 'PUT',
      payload: [Object]
    }
  ],
  toJSON: [Function: toJSON]
}

Here is the code of the saga being implemented

export function* showCurrentResearch() {
  const currentResearch = yield call(getCurrentResearch)
  if (!currentResearch) {
    return
  }

  const { id } = currentResearch
  if (!id) {
    yield put(throwError('RESEARCH: Invalid research format. No id found'))
    return
  }

  try {
    const researchStatus = yield call(researchService.getResearchStatus, id)
    if (researchStatus !== 'ACTIVE') {
      yield put(removeResearch(id))
      yield call(showCurrentResearch)
      return
    }

    yield call(executeResearchDialogFlow, activeResearch)
  } catch (error) {
    yield put(throwError(`Error getting research #${id} status: ${error}`))
  }
}

The code of the test-suit that exhibited the error:

it('throws error if service request fails', () => {
  const id = 1
  const error = 'mocked-error-message'

  return expectSaga(showCurrentResearch)
    .provide([
      [matchers.call.fn(getCurrentResearch), { id }],
      [matchers.call.fn(researchService.getResearchStatus), Promise.reject(error)],
    ])
    .call(getCurrentResearch)
    .call.like(researchService.getResearchStatus)
    .put(throwError(`Error getting research #${id} status: ${error}`))
    .run()
    .then((result) => {
      console.log(JSON.stringify(result))
    })
})