ctimmerm / axios-mock-adapter

Axios adapter that allows to easily mock requests
MIT License
3.46k stars 245 forks source link

Unhandled promise rejection ... Error: Network Error #122

Open smallstepstoday opened 6 years ago

smallstepstoday commented 6 years ago

I have spent the weekend attempting to resolve this issue, and I believe this to be a bug in axios-mock-adapter. Having said that, if that is not the case, I hope that someone could point out the flaw in the code below. The documentation on the networkError() function is sparse to say the least, and no amount of stackoverflow.com goodness seems to shed any additional light on this one.

My project uses axios-mock-adapter at 1.14.1, and I am attempting to simulate a network error (also a timeout, but one thing at a time). I have attempted to cover all the bases in an attempt to trap the Unhandled Promise Rejection that is reported by Node, so the code below is perhaps a bit more verbose than it needs to be. (Happy to receive any pointers...)

  return axios(data)
    .then((response) => {
      console.log('response',response)
      if (response.data.order === null) { // No order placed yet
        console.log('response.data.order === null', response.data.order === null)
        return {
          status: response.status,
          id: memberId,
          ...
        };
      }
      const { results_ready } = response.data.order; // eslint-disable-line camelcase
      console.log('results_ready', results_ready);
      return {
        status: response.status,
        id: memberId,
        dataReady: results_ready !== null, // eslint-disable-line camelcase
      };
    }, (err) => {
      console.log('err', err);
      if (err.response && err.response.data) {
        console.log('err.response && err.response.data');
        return {
          status: err.response.status,
          message: err.response.data.message,
        };
      }
      // Handle network errors and timeouts
      if (err.code === 'ECONNABORTED' || !err.response.status) {
        console.log('err.code === ECONNABORTED || !err.response.status');
        const result = {
          status: 500,
          message: 'Failed due to network errors',
        };
        console.log('returning', result);
        return result;
      }
      console.log('throwing', err);
      // Throw the remainder
      throw err;
    })
    .catch((err) => {
      console.log('catching', err);
      if (err.response && err.response.data) {
        console.log('catch: err.response && err.response.data');
        // This is a programmer error, or a change in configuration on vendor API side.
        if (err.response.status === 401) {
          console.log('catch: err.response.status === 401');
          throw err;
        }
        // Some other error occurred. Return the stock error information.
        return {
          status: err.response.status,
          message: err.response.data.message,
        };
      }
      console.log('throwing', err);
      debug(err);
      throw err;
    });

The code to test is a follows:

  test('it handles network errors correctly', (done) => {
    const expectedResponse = {
      status: 500,
      message: 'Failed due to network errors',
    };

    mock.onGet(ORDER_API_CALL).networkError();

    getOrderStatus(VALID_MEMBER_ID)
      .then((resp) => {
        console.log('call return', resp);
        expect(resp).toEqual(expectedResponse);
        done();
      }, err => console.log('mystery', err))
      .catch((err) => {
        console.log('call error caught', err); // eslint-disable-line no-console
        done();
      });
  });

The output received when the test runs is:

(node:14986) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1190): Error: Network Error
    ...
    console.log api/.../index.js:129
      err { Error: Request failed with status code undefined
          at createErrorResponse (/home/.../node_modules/axios-mock-adapter/src/utils.js:117:15)
          at Object.settle (/home/.../node_modules/axios-mock-adapter/src/utils.js:97:16)
          at handleRequest (/home/.../node_modules/axios-mock-adapter/src/handle_request.js:51:15)
          at /home/.../node_modules/axios-mock-adapter/src/index.js:18:9
          at new Promise (<anonymous>)
          at MockAdapter.<anonymous> (/home/.../node_modules/axios-mock-adapter/src/index.js:17:14)
          at dispatchRequest (/home/.../node_modules/axios/lib/core/dispatchRequest.js:59:10)
          at <anonymous>
          at process._tickCallback (internal/process/next_tick.js:188:7)
        config: 
         { adapter: null,
           transformRequest: { '0': [Function: transformRequest] },
           transformResponse: { '0': [Function: transformResponse] },
           timeout: 0,
           xsrfCookieName: 'XSRF-TOKEN',
           xsrfHeaderName: 'X-XSRF-TOKEN',
           maxContentLength: -1,
           validateStatus: [Function: validateStatus],
           headers: 
            { Accept: 'application/json, text/plain, */*',
              Authorization: 'API-KEY undefined' },
           method: 'get',
           url: 'https://.../order',
           data: undefined },
        response: 
         { status: undefined,
           data: undefined,
           headers: undefined,
           config: 
            { adapter: null,
              transformRequest: [Object],
              transformResponse: [Object],
              timeout: 0,
              xsrfCookieName: 'XSRF-TOKEN',
              xsrfHeaderName: 'X-XSRF-TOKEN',
              maxContentLength: -1,
              validateStatus: [Function: validateStatus],
              headers: [Object],
              method: 'get',
              url: 'https://.../order',
              data: undefined } } }
    console.log api/.../index.js:139
      err.code === ECONNABORTED || !err.response.status
    console.log api/.../index.js:144
      returning { status: 500, message: 'Failed due to network errors' }
    console.log api/.../....test.js:323
      call return { status: 500, message: 'Failed due to network errors' }

Thanks for looking into this issue. Has me stumped!

Liu233w commented 6 years ago

@smallstepstoday Hi, I believe that I have the same issue here. Test code:

const axiosMockAdapter = require('axios-mock-adapter')
const axios = require('axios')

const axiosMock = new axiosMockAdapter(axios, {delayResponse: 200})

axiosMock.onGet('/aaa').networkError()
axiosMock.onGet('/bbb').timeout()
axiosMock.onGet('/ccc').reply(500)

test('aaa', done => {
  axios.get('/aaa')
    .then(() => done())
    .catch(e => done())
})

test('bbb', done => {
  axios.get('/bbb')
    .then(() => done())
    .catch(e => done())
})

test('ccc', done => {
  axios.get('/ccc')
    .then(() => done())
    .catch(e => done())
})

test case aaa and bbb failed while ccc passed.

environment:

error log of aaa:

Error: Network Error at Array. (D:\Sources\project\acm-statistics\crawler\node_modules\axios-mock-adapter\src\index.js:96:23) at handleRequest (D:\Sources\project\acm-statistics\crawler\node_modules\axios-mock-adapter\src\handle_request.js:50:30) at D:\Sources\project\acm-statistics\crawler\node_modules\axios-mock-adapter\src\index.js:18:9 at new Promise () at MockAdapter. (D:\Sources\project\acm-statistics\crawler\node_modules\axios-mock-adapter\src\index.js:17:14) at dispatchRequest (D:\Sources\project\acm-statistics\crawler\node_modules\axios\lib\core\dispatchRequest.js:59:10) at at process._tickCallback (internal/process/next_tick.js:188:7)

If I delete {delayResponse: 200}, test case aaa and bbb will pass but I'll get a warning like below:

(node:27124) UnhandledPromiseRejectionWarning: Error: Network Error (node:27124) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:27124) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Liu233w commented 6 years ago

Well, I just tested it on mocha, and all the test passed. While jest fails only when the testEnvironment is setted to node.

According to jest's document, the default environment in Jest is a browser-like environment through jsdom. It seems like axios-mock-adapter behaves differently from node environment and 'browser-like' environment. I don't know if it's a bug or feature.

I created a repo to reproduce it.

smallstepstoday commented 6 years ago

This is running under jsdom with react-scripts 1.1.1, and so should not fail.

jd-carroll commented 6 years ago

This could be a duplicate of #116

Could you please try the steps outlined there and confirm? (Note: See this comment)