Skip to content

Instantly share code, notes, and snippets.

@khanghoang
Last active April 21, 2017 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save khanghoang/dd4179fd80f9acec062789cb37d74cda to your computer and use it in GitHub Desktop.
Save khanghoang/dd4179fd80f9acec062789cb37d74cda to your computer and use it in GitHub Desktop.
Redux-api-call test utils

Pain point #1

Redux-api-call's makeFetchAction comes with its own dataSelector, but in real world, we can't just grab and use it. We may need to write our selector which bases on dataSelector.
Then when we write our unit test of that selector, we have to setup state which has api_calls property and the response. For example:

const state = {
  // some other properties
  api_calls: {
    fooApi: {
      // meta data of this api call
      data: {        
        foo: 'bar'
      }
    }
  }
}

Number 1: Setting up this state is very tedious task for us because what we really want to test is just selector of our response ( which is { foo: 'bar' } in this case, but we end up making (almost) the whole state object.

Number 2: We want to write test for our selector, not makeFetchAction's selector.

How can we do better? Imagine that we have something like this API to construct our state base on actionCreator from makeFetchAction

import setAPIResponse from './testUtils/setAPIResponse';

const state = setAPIResponse(actionCreator, {
  foo: "bar"
}).withState({});

The state will be exactly the same with the state above. Want to test selector which selects from more than 1 selector, here is your recipe.

import setAPIResponse from './testUtils/setAPIResponse';

const state = setAPIResponse(actionCreator, {
  foo: "bar"
})
.setAPIResponse(anotherActionCreator, {
  duck: {
    age: 27,
    name: 'Melon'
  }
})
.withState({});

You know what I mean, right? And you can also pass the inititalState or seedState along with it by calling withState.
Here is the implemetation of the setAPIResponse.

import { get } from 'lodash';

/* eslint-disable */
class MockAPIResponse {
  constructor(state = {}) {
    this.state = state;
  }

  setAPIResponse = (actionCreator, json) => {
    const action = actionCreator();
    const symbol = Object.getOwnPropertySymbols(action)[0];
    // eslint-disable-next-line immutable/no-let
    let apiName;
    if (symbol) {
      apiName = action[symbol].name;
    } else {
      apiName = action[0].payload.name;
    }
    this.val = {
      [apiName]: {
        data: json,
      },
    };
    return new MockAPIResponse(this.withState(this.state));
  }

  withState = (state) => {
    return {
      ...this.state,
      ...state,
      api_calls: {
        ...state.api_calls,
        ...this.state.api_calls,
        ...get(state, 'api_calls', {}),
        ...this.val,
      },
    };
  }
}

export default new MockAPIResponse().setAPIResponse;

Good luck and happy testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment