redux-axios-middleware icon indicating copy to clipboard operation
redux-axios-middleware copied to clipboard

getState() is cached no matter that store was changed

Open mgenov opened this issue 8 years ago • 3 comments

As the redux-axios-middleware is caching configuration per name of the client it's not possible different store to be passed for testing unless different client is created for each test which sounds overwhelming.

Here is a sample test that illustrates the problem:

import axios from 'axios';
import axiosMiddleware from '../src/middleware';

import { expect } from 'chai';
import configureMockStore from 'redux-mock-store';
import MockAdapter from 'axios-mock-adapter';

const options = {
  returnRejectedPromiseOnError: true,

  interceptors: {
    request: [
      ({ getState, dispatch }, config) => {
        console.log('state in interceptor: ', getState());
        config.headers['Authorization'] = 'Bearer ' + getState().access_token;
        return config;
      }
    ],
    response: [
      {
        success: ({ dispatch }, response) => {
          return response;
        },
        error: ({ dispatch, getSourceAction }, error) => {
          return Promise.reject(error);
        }
      }
    ]
  }
};

const middleware = axiosMiddleware(axios, options);

describe('axiosMiddleware', () => {
  const mockAxiosClient = new MockAdapter(axios);
  const mockStore = configureMockStore([middleware]);
  const mockAdapter = mockAxiosClient.adapter();

  afterEach(() => {
    mockAxiosClient.reset();
  });

  after(() => {
    mockAxiosClient.restore();
  });

  it('attaches authorization header on each request', () => {
    let got;

    mockAxiosClient.onGet('/test').reply(config => {
      got = config.headers['Authorization'];
      return [200];
    });

    const action = () => {
      return {
        type: 'LOAD',
        payload: {
          request: {
            url: '/test'
          }
        }
      };
    };

    const store = mockStore({ access_token: '::access_token::' });

    return store.dispatch(action()).then(() => {
      expect(got).to.equal('Bearer ::access_token::');
    });
  });


  it('attaches another authorization header on each request', () => {
    let got;

    mockAxiosClient.onGet('/test2').reply(config => {
      got = config.headers['Authorization'];
      return [200];
    });

    const action = () => {
      return {
        type: 'ANOTHER_LOAD_ACTION',
        payload: {
          request: {
            url: '/test2'
          }
        }
      };
    };

    const store = mockStore({ access_token: '::another_access_token::' });

    return store.dispatch(action()).then(() => {
      expect(got).to.equal('Bearer ::another_access_token::');
    });
  });
});

The following test fails with:

  1) axiosMiddleware attaches another authorization header on each request:

      AssertionError: expected 'Bearer ::access_token::' to equal 'Bearer ::another_access_token::'
      + expected - actual

      -Bearer ::access_token::
      +Bearer ::another_access_token::

e.g the test of first test is used as it caches the store instance that was passed no matter that another store is created using mockStore.

As better feedback I've added few loggings in the interceptor too:

return ({ getState, dispatch }) => next => action => {
    if (!middlewareOptions.isAxiosRequest(action)) {
      return next(action);
    }
    console.log(`action: ${action.type}, store value: `, getState());

and this is the execution log:

  axiosMiddleware
action: LOAD, store value:  { access_token: '::access_token::' } -> this is in the middleware
state in interceptor:  { access_token: '::access_token::' } -> this is in the interceptor 
    ✓ attaches authorization header on each request
action: ANOTHER_LOAD_ACTION, store value:  { access_token: '::another_access_token::' } -> this is in the middleware
state in interceptor:  { access_token: '::access_token::' } -> this is in the interceptor

mgenov avatar Aug 21 '17 14:08 mgenov

As possible solution middleware may keep reference to interceptors and to re-attach them before each request but this will add additional overhead.

Any thoughts on this ?

mgenov avatar Aug 22 '17 11:08 mgenov

I have the same issue

ValentinBlokhin avatar Oct 18 '17 08:10 ValentinBlokhin

I've resolved it temporary in the project side by creating a new .spec.js file for each scenario but it's overwhelming if you have many scenarios.

mgenov avatar Oct 18 '17 08:10 mgenov