Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mock failed with 404 Error #116

Open
kirankbs opened this issue Feb 14, 2018 · 63 comments
Open

Mock failed with 404 Error #116

kirankbs opened this issue Feb 14, 2018 · 63 comments

Comments

@kirankbs
Copy link

i am using axios-mock-adapter to mock tests in react app.
got below error while running single test but it is passing in case of all tests.

/home/dev/code/client/node_modules/react-scripts/scripts/test.js:22
throw err;
^
Error: Request failed with status code 404
at createErrorResponse (/home/dev/code/client/node_modules/axios-mock-adapter/src/utils.js:122:15)
at Object.settle (/home/dev/code/client/node_modules/axios-mock-adapter/src/utils.js:102:16)
at handleRequest (/home/dev/code/client/node_modules/axios-mock-adapter/src/handle_request.js:69:11)
at /home/dev/code/client/node_modules/axios-mock-adapter/src/index.js:16:9
at MockAdapter. (/home/dev/code/client/node_modules/axios-mock-adapter/src/index.js:15:14)
at dispatchRequest (/home/dev/code/client/node_modules/axios/lib/core/dispatchRequest.js:52:10)

@ctimmerm
Copy link
Owner

I can't do much with the little information that you're giving, but likely the code that adds the mock handlers does not get run when running a single test.

@HawiCaesar
Copy link

HawiCaesar commented Feb 20, 2018

I got a similar error to @kirankbs.

My code seems to not evaluate the axios.get bit with mock

Here is a sample of my code

export const getLocationInfo = (lat, lng) => {
  return ((dispatch) => {
    return axios.get(process.env.API_LOCATION_URL+`/json?latlng=${lat},${lng}&key=${process.env.GOOGLE_KEY}`)
      .then((response) => {
        dispatch({ type: "FETCHED_LOCATION_INFO", payload: response.data.results });
      }).catch((error) => {
        dispatch({ type: 'FAILED_FETCHING_LOCATION_INFO', payload: error });
      });
  });
};

The test code below

it('should dispatch FETCHED_LOCATION_INFO when getLocationInfo completes successfully', (done) => {

    let middlewares = [thunk];
    let mockStore = configureMockStore(middlewares);
    let store = mockStore({});
    mock = new MockAdapter(axios);
    mock.onGet('http://maps.google.com/').reply(200, {
      data: {
        results: [
          {
            formatted_address: 'Abc Rd, City, Country'
          }
        ]
      }
    });

    store.dispatch(getLocationInfo(-5.2177265, 12.9889)).then(() => {
       const actualActions = store.getActions();
       expect(expectedActions[0].type).toEqual(actualActions[0].type);
       done();
    });
  });

This is my error

[ { type: 'FAILED_FETCHING_LOCATION_INFO',
    payload: { Error: Request failed with status code 404
    at createErrorResponse (/Users/brianhawi/Documents/learning-projects/weather-app/node_modules/axios-mock-adapter/src/utils.js:117:15)
    at Object.settle (/Users/brianhawi/Documents/learning-projects/weather-app/node_modules/axios-mock-adapter/src/utils.js:97:16)
    at handleRequest (/Users/brianhawi/Documents/learning-projects/weather-app/node_modules/axios-mock-adapter/src/handle_request.js:69:11)
    at /Users/brianhawi/Documents/learning-projects/weather-app/node_modules/axios-mock-adapter/src/index.js:18:9
    at new Promise (<anonymous>)
    at MockAdapter.<anonymous> (/Users/brianhawi/Documents/learning-projects/weather-app/node_modules/axios-mock-adapter/src/index.js:17:14)
    at dispatchRequest (/Users/brianhawi/Documents/learning-projects/weather-app/node_modules/axios/lib/core/dispatchRequest.js:52:10)
    at <anonymous> config: [Object], response: [Object] } } ]

@djalmaaraujo
Copy link

Getting the same error as mentioned above

        mock = new MockAdapter(requestInstance)
        
        mock.onPost(`${SERVER_URL}/api/cart`).reply(200, 'cart')

        mock.onGet(`${SERVER_URL}/api/quoteDetails/product/`).reply(200, { data: {x: 2} })

@davidlewallen
Copy link
Contributor

@djalmaaraujo What does your http request look like for POST and GET?

@djalmaaraujo
Copy link

@davidlewallen Not sure if I understood the question.

@davidlewallen
Copy link
Contributor

@djalmaaraujo can you show me the code that is making the request to ${SERVER_URL}/api/car

@djalmaaraujo
Copy link

djalmaaraujo commented Feb 20, 2018

@davidlewallen

/**
 * Axios Request Instance (Move to config later)
 */
export const requestInstance = axios.create({
  baseURL: SERVER_URL,
  timeout: 20000,
  headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
})
/**
 * Fetch QuoteDetails using ISBN
 */
export const buybackFetchQuoteDetails = (isbn) => {
  return (dispatch) => {
    if (!isbn) {
      return dispatch({
        type: 'BUYBACK/quoteDetailsFail',
        payload: 'Invalid ISBN, please try another ISBN.'
      })
    }

    dispatch({
      type: 'BUYBACK/loading'
    })

    return createOrReturnCart().then((cart) => {
      dispatch({
        type: 'BUYBACK/createOrReturnCart',
        payload: cart
      })

      return requestInstance.get(`api/quoteDetails/product/${isbn}`)
        .then((response) => {
          dispatch({
            type: 'BUYBACK/quoteDetailsSuccess',
            payload: response.data
          })
        })
        .catch((error) => {
          console.log('I ALWAYS GET CATCH HERE')
          dispatch({
            type: 'BUYBACK/quoteDetailsFail',
            payload: (error.response.data && error.response.data.error) ? error.response.data.error : 'Invalid ISBN, please try another ISBN.'
          })

          return error
        })
    })
  }
}
export const createOrReturnCart = () => {
  return requestInstance.post('api/cart')
    .then((response) => response.data)
    .catch(err => err)
}

Spec:

describe('async quote details', () => {
      let mock;
      let dispatchMockSpy;

      beforeEach(() => {
        mock = new MockAdapter(requestInstance)
        dispatchMockSpy = sinon.spy()

        mock
          .onPost(`${SERVER_URL}/api/cart`).reply(200, 'cart')
          .onGet(`${SERVER_URL}/api/quoteDetails/product/123456`).reply(200, 'quoteDetailsData')
      })

      afterEach(() => {
        mock.reset()
      })

      it("should dispatch data", () => {
        const buybackFetchQuoteDetailsMock = buybackFetchQuoteDetails('123456')
        buybackFetchQuoteDetailsMock(dispatchMockSpy).then((x) => {
          expect(dispatchMockSpy.calledWith({
            type: 'BUYBACK/loading'
          })).toBeTruthy()

          expect(dispatchMockSpy.calledWith({
            type: 'BUYBACK/createOrReturnCart',
            payload: 'cart'
          })).toBeTruthy()
        }).catch((x) => console.log(x))
      })
    })

@davidlewallen
Copy link
Contributor

@djalmaaraujo the code you link has no GET request to ${SERVER_URL}/api/cart or a POST request to ${SERVER_URL}/api/quoteDetails/product/

@djalmaaraujo
Copy link

djalmaaraujo commented Feb 21, 2018

@davidlewallen I updated the code above, please take a look. Just added what is requestInstance. In the code above, the /api/cart works, but the get, does not.

@davidlewallen
Copy link
Contributor

davidlewallen commented Feb 21, 2018

@djalmaaraujo I think its how you are using the base url. You can console.log(mock.handlers.get) to see if the right url is saved in the array.

I just mocked this up and it works just fine isolated from Redux.

  fit('it should work', () => {
    const SERVER_URL = '/test';
    const requestInstance = axios.create({
      baseURL: SERVER_URL,
      timeout: 2000,
      headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
    });

    const newMock = new MockAdapter(requestInstance);
    newMock.onGet('/api/quoteDetails/product/123456').reply(200, 'quoteDetailsData');

    requestInstance.get('api/quoteDetails/product/123456')
      .then(response => console.log('response', response))
      .catch(error => console.log('error', error));
  });

Response console.log

 response { status: 200,
      data: 'quoteDetailsData',
      headers: undefined,
      config:
       { adapter: null,
         transformRequest: { '0': [Function: transformRequest] },
         transformResponse: { '0': [Function: transformResponse] },
         timeout: 2000,
         xsrfCookieName: 'XSRF-TOKEN',
         xsrfHeaderName: 'X-XSRF-TOKEN',
         maxContentLength: -1,
         validateStatus: [Function: validateStatus],
         headers:
          { Accept: 'application/json',
            'Content-Type': 'application/json' },
         baseURL: '/test',
         method: 'get',
         url: '/api/quoteDetails/product/123456',
         data: undefined } }

@djalmaaraujo
Copy link

@davidlewallen But why the /api/cart works and the other don't?

@djalmaaraujo
Copy link

I just console.log requestInstance

 Console

    console.log src/Actions/BuyBackActions.test.js:49
      { [Function: wrap]
        request: [Function: wrap],
        delete: [Function: wrap],
        get: [Function: wrap],
        head: [Function: wrap],
        options: [Function: wrap],
        post: [Function: wrap],
        put: [Function: wrap],
        patch: [Function: wrap],
        defaults:
         { adapter: [Function: xhrAdapter],
           transformRequest: [ [Function: transformRequest] ],
           transformResponse: [ [Function: transformResponse] ],
           timeout: 20000,
           xsrfCookieName: 'XSRF-TOKEN',
           xsrfHeaderName: 'X-XSRF-TOKEN',
           maxContentLength: -1,
           validateStatus: [Function: validateStatus],
           headers:
            { common: [Object],
              delete: {},
              get: {},
              head: {},
              post: [Object],
              put: [Object],
              patch: [Object],
              Accept: 'application/json',
              'Content-Type': 'application/json' },
           baseURL: 'http://localhost:8080/buyback' },
        interceptors:
         { request: InterceptorManager { handlers: [] },
           response: InterceptorManager { handlers: [] } } }
    console.log src/Actions/BuyBackActions.js:46

@davidlewallen
Copy link
Contributor

@djalmaaraujo add console.log(JSON.stringify(mock.handlers, null, 2)) inside of your beforeEach but after the mock calls and post that up

@djalmaaraujo
Copy link

djalmaaraujo commented Feb 21, 2018

@davidlewallen

   Console

    console.log src/Actions/BuyBackActions.test.js:59
      {
        "get": [
          [
            "http://localhost:8080/buyback/api/quoteDetails/product/123456",
            null,
            200,
            "quoteDetailsData",
            null
          ]
        ],
        "post": [
          [
            "http://localhost:8080/buyback/api/cart",
            null,
            200,
            "cart",
            null
          ]
        ],
        "head": [],
        "delete": [],
        "patch": [],
        "put": []
      }

I was using onAny, just fixed. see now

@djalmaaraujo
Copy link

@davidlewallen The error returned in the catch (in the src code), is this:

{ Error: Request failed with status code 404
          at createErrorResponse (/Users/cooper/dev/valore/valore-buyback-webapp/client/node_modules/axios-mock-adapter/src/utils.js:110:15)
          at Object.settle (/Users/cooper/dev/valore/valore-buyback-webapp/client/node_modules/axios-mock-adapter/src/utils.js:90:16)
          at handleRequest (/Users/cooper/dev/valore/valore-buyback-webapp/client/node_modules/axios-mock-adapter/src/handle_request.js:55:11)
          at /Users/cooper/dev/valore/valore-buyback-webapp/client/node_modules/axios-mock-adapter/src/index.js:16:9
          at new Promise (<anonymous>)
          at MockAdapter.<anonymous> (/Users/cooper/dev/valore/valore-buyback-webapp/client/node_modules/axios-mock-adapter/src/index.js:15:14)
          at dispatchRequest (/Users/cooper/dev/valore/valore-buyback-webapp/client/node_modules/axios/lib/core/dispatchRequest.js:59:10)
          at <anonymous>
        config:
         { adapter: null,
           transformRequest: { '0': [Function: transformRequest] },
           transformResponse: { '0': [Function: transformResponse] },
           timeout: 20000,
           xsrfCookieName: 'XSRF-TOKEN',
           xsrfHeaderName: 'X-XSRF-TOKEN',
           maxContentLength: -1,
           validateStatus: [Function: validateStatus],
           headers:
            { Accept: 'application/json',
              'Content-Type': 'application/json' },
           baseURL: 'http://localhost:8080/buyback',
           method: 'get',
           url: '/api/quoteDetails/product/123456',
           data: undefined },
        response:
         { status: 404,
           config:
            { adapter: null,
              transformRequest: [Object],
              transformResponse: [Object],
              timeout: 20000,
              xsrfCookieName: 'XSRF-TOKEN',
              xsrfHeaderName: 'X-XSRF-TOKEN',
              maxContentLength: -1,
              validateStatus: [Function: validateStatus],
              headers: [Object],
              baseURL: 'http://localhost:8080/buyback',
              method: 'get',
              url: '/api/quoteDetails/product/123456',
              data: undefined },
           data: undefined } }

@davidlewallen
Copy link
Contributor

@djalmaaraujo try removing the ${SERVER_URL} and see if that helps?

@djalmaaraujo
Copy link

@davidlewallen Same thing. Still have the /api/cart working, but not the other request. The difference is that this request is inside another promise. maybe that's the reason?

@davidlewallen
Copy link
Contributor

davidlewallen commented Feb 21, 2018

@djalmaaraujo hmm I am seeing something a little different on my end. Try adding this to createOrReturnCart function:

const createOrReturnCart = () => requestInstance.post('/api/cart')
      .then(response => response)
      .catch((error) => {
        console.log('error', error)
        return error;
      });

And see if you get an error there?

@djalmaaraujo
Copy link

djalmaaraujo commented Feb 21, 2018

@davidlewallen I changed a little bit the code to show you the test results:

/**
 * Fetch QuoteDetails using ISBN
 */
export const buybackFetchQuoteDetails = (isbn) => {
  return (dispatch) => {
    if (!isbn) {
      return dispatch({
        type: 'BUYBACK/quoteDetailsFail',
        payload: 'Invalid ISBN, please try another ISBN.'
      })
    }

    dispatch({
      type: 'BUYBACK/loading'
    })

    return createOrReturnCart().then((cart) => {
      console.log('CART PASSED')
      dispatch({
        type: 'BUYBACK/createOrReturnCart',
        payload: cart
      })

      return requestInstance.get(`api/quoteDetails/product/${isbn}`)
        .then((response) => {
          console.log('QUOTE DETAILS RESOLVED')
          dispatch({
            type: 'BUYBACK/quoteDetailsSuccess',
            payload: response.data
          })
        })
        .catch((error) => {
          console.log('ERROR IN QUOTE DETAILS')
          dispatch({
            type: 'BUYBACK/quoteDetailsFail',
            payload: (error.response.data && error.response.data.error) ? error.response.data.error : 'Invalid ISBN, please try another ISBN.'
          })

          return error
        })
    })
  }
}

const createOrReturnCart = () => requestInstance.post('/api/cart')
  .then(response => response)
  .catch((error) => {
    console.log('error', error)
    return error;
  })

SPEC:

 PASS  src/Containers/BuyBackContainer/BuyBackContainer.test.js
 PASS  src/Actions/BuyBackActions.test.js
  ● Console

    console.log src/Actions/BuyBackActions.js:32
      CART PASSED
    console.log src/Actions/BuyBackActions.js:47
      ERROR IN QUOTE DETAILS
    console.log src/Actions/BuyBackActions.test.js:81
      { Error: expect(received).toBeTruthy()

      Expected value to be truthy, instead received
        false
          at buybackFetchQuoteDetailsMock.then.x (/Users/cooper/dev/valore/valore-buyback-webapp/client/src/Actions/BuyBackActions.test.js:75:11)
          at <anonymous> matcherResult: { message: [Function], pass: false } }


Test Suites: 2 passed, 2 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        0.938s, estimated 1s
Ran all test suites related to changed files.

Watch Usage: Press w to show more.

@davidlewallen
Copy link
Contributor

@djalmaaraujo One last thing before I give up.
Make these changes

mock
  .onPost('/api/cart').reply(200, 'cart')
  .onGet(/api\/quoteDetails\/product\/.+/).reply(200, 'quoteDetailsData')

See if that give any better results

@djalmaaraujo
Copy link

djalmaaraujo commented Feb 21, 2018

@davidlewallen Sorry the delay, but still failing. I will give up, check for another alternative. Thanks for your time, honestly.

@djalmaaraujo
Copy link

@davidlewallen I was able to make it work. I split the method into 2 methods and tested separately. I can't tell you exactly what is the issue, but I believe it's a bug from this mock-adapter, when you have a nested promise level, it's like the mock is not aware of that request.

@djalmaaraujo
Copy link

@davidlewallen After another time facing the same issue, I think the solution is to pass (done) and wait for the correct ajax to finish.

Check this example, and it works:

it('should return cartItem data and dispatch BUYBACK/cartAddItem', (done) => {
      mock
        .onPost('/api/cart').reply(200, { id: 5 })
        .onPost('/api/cart/addItem', {cartId: 5, isbn: 123456}).reply(200, expectedResponse)

      buybackAddItemToCart(5, {productCode: '123456'}, dispatchMockSpy)
        .then(() => {
          expect(dispatchMockSpy.calledWith({
            type: 'BUYBACK/cartLoading'
          })).toBeTruthy()

          expect(dispatchMockSpy.args[1][0].payload).toEqual({
            cartItem: expectedResponse,
            quoteDetails: { productCode: '123456' }
          })

          done()
        })
        .catch(err => {
          done.fail('Should not call catch')
          console.log(err)
        })
    })

In this case, I am always getting 404 for the /api/cart... but with the done, now it works.

@pmgmendes
Copy link

pmgmendes commented May 4, 2018

Hi,
Sorry for reviving the thread but because its still open it could make sense to reuse it as I'm facing the same issues described by @kirankbs, @HawiCaesar and @djalmaaraujo.

I'm not able to get it to work even with @djalmaaraujo's solution using the done callback.

What could be missing here?

Dependencies:
[email protected]
[email protected]

The method under test:

export const createResource = field => {
  return axios
    .post(
      "http://app.io/api/resource",
      JSON.stringify({
        field
      })
    );
};

The test:

it("should create a resource", () => {
  const fieldValue = "field value", 
    resourceName = "resource name";

  new MockAdapter(axios)
    .onPost("http://app.io/api/resource", { field: fieldValue })
    .reply(200, { field: fieldValue, name: resourceName});

  return createResource(field).then(({ field, name }) => {
    expect(field).toBe(fieldvalue);
    expect(name).toBe(resourceName);
  }).catch(error => {
    console.error(error);
    return error;
  });
});

The error:

{ Error: Request failed with status code 404
  at createErrorResponse (/home/pmgmendes/repo/node_modules/axios-mock-adapter/src/utils.js:117:15)
  at Object.settle (/home/pmgmendes/repo/node_modules/axios-mock-adapter/src/utils.js:97:16)
  at handleRequest (/home/pmgmendes/repo/node_modules/axios-mock-adapter/src/handle_request.js:73:11)
  at /home/pmgmendes/repo/node_modules/axios-mock-adapter/src/index.js:18:9
  at new Promise (<anonymous>)
  at MockAdapter.<anonymous> (/home/pmgmendes/repo/node_modules/axios-mock-adapter/src/index.js:17:14)
  at dispatchRequest (/home/pmgmendes/repo/node_modules/axios/lib/core/dispatchRequest.js:52:10)
  at <anonymous>
  at process._tickCallback (internal/process/next_tick.js:118:7)
config: 
  { 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, */*',
      'Content-Type': 'application/x-www-form-urlencoded' },
    method: 'post',
    url: 'http://app.io/api/resource',
    data: '{"field":"field value"}' 
  },
response: 
  { status: 404,
    config: 
    { transformRequest: [Object],
      transformResponse: [Object],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      validateStatus: [Function: validateStatus],
      headers: [Object],
      method: 'post',
      url: 'http://app.io/api/resource',
      data: '{"field":"field value"}' 
    },
    data: undefined 
  } 
}

@william-bratches
Copy link

+1

I'm using axios in a node service layer that utilizes axios.create to put a common baseUrl in all axios requests. The mock-adapter seems to failing to work in my case as well.

@mdiflorio
Copy link

Getting exactly the same issue with a nested promise.

@pmgmendes
Copy link

After debugging session focused on utils.js#findHandler() I came to conclusion that the generated POST body didn't match the expected payload provided to the axios mock thus causing the 404.

The functions used within findHandler method would benefit immensely by having some verbose logging. It would be much easier to understand what's the real reason behind a possible 404.

@jd-carroll
Copy link

What version of node are people using? I am also facing the same issue and I believe I have chased it down to Utils.findHandler.

My environment:

> process.versions
{ ...
  node: '8.9.4',
  v8: '6.1.534.50',
  ... }

To demonstrate the issue, go into your node_modules and update your utils.js to the following:

function find(array, predicate) {
  var length = array.length;
  for (var i = 0; i < length; i++) {
    var value = array[i];
    const pred = predicate(value);
    if (pred) return value;
    else console.log(`Failed to match value: ${pred}`);
  }
}

// ...

function isBodyOrParametersMatching(method, body, parameters, required) {
  console.log('isBodyOrParametersMatching');
// ...

What you'll see is that the predicate fails to match with a log statement of:
Failed to match value: undefined
Further, you will not see isBodyOrParametersMatching printed in the logs either.

My theory is that there is a bug in the way V8 handles [arbitrarily] complex bitwise statements. I've seen this behavior (where it returns undefined instead of a boolean) in another of our internal projects (completely unrelated to axios-mock-adapter).

Once you confirmed the behavior above, try replacing the findHandler method with the following:

function findHandler(handlers, method, url, body, parameters, headers, baseURL) {
  console.log(arguments);
  console.log(handlers);
  return find(handlers[method.toLowerCase()], function(handler) {
    let urlMatch = false;
    if (typeof handler[0] === 'string') {
      urlMatch = isUrlMatching(url, handler[0]) || isUrlMatching(combineUrls(baseURL, url), handler[0]);
    } else if (handler[0] instanceof RegExp) {
      urlMatch = handler[0].test(url) || handler[0].test(combineUrls(baseURL, url));
    }
    const bodyParamMatch = urlMatch && isBodyOrParametersMatching(method, body, parameters, handler[1]);
    const headersMatch = bodyParamMatch && isRequestHeadersMatching(headers, handler[2]);
    return headersMatch
  });
}

You'll notice that isBodyOrParametersMatching is printed in the logs as expected and the predicate matches successfully.

My theory is that there is a bug in the way the V8 runtime parse boolean statements. Specifically, when the V8 runtime parses a bitwise boolean statement where there is a parentheses group followed by a method invocation. For whatever reason, I believe V8 falls on its face and returns undefined where that value should otherwise be unattainable.

As I said earlier, I've seen this in an internal ReactJS app (which was not using axios) running in the latest Chrome browser. I'll see if I can put together a sample app to test my theory.

I'll also put together a PR to solve this issue (with the updated findHandler method). Though it will have to wait until I get home tonight as we are unable to push anything to github from our office 😢

PS.- If someone else wants to take the code above and create a PR before I do, please feel free. (Just make sure to reference this issue)

@zarapustra
Copy link

zarapustra commented Mar 7, 2019

in my case running npm test instead of npm run test and replacing the URL with the correct value in an env variable used in the code.

@mei33
Copy link

mei33 commented Mar 27, 2019

Hi! I have same issue - I have two tests in describe block and each of them runs perfectly, but running both of them leads to fail. My current approach is:

_axiosMockAdapter.js:

import Vue from "vue";
import VueAxios from "vue-axios";
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';

Vue.use(VueAxios, axios);

export default new MockAdapter(axios);

test.js:

import mockAxios from './_axiosMockAdapter'
import axios from 'axios';
import global from '../global';

describe('accessing to server', () => {
    it('sends data on "Save" to predefined url', async done => {
        const wrapper = mount(global);
        const url = wrapper.vm.$data.url;

        const saveButton = wrapper.find({ref: 'save'});
        const fakeData = {...some data...}

        wrapper.vm.$nextTick(() => {
            saveButton.trigger('click');
            expect(spyAxiosOnPut).toHaveBeenCalledWith(url, {...fakeData});
            const spyAxiosOnPut = jest.spyOn(axios, 'put');
            spyAxiosOnPut.mockClear();
             done();
        });
    })

    it('fetches data from predefined url when created', async done => {
        const flushPromises = () => new Promise(resolve => setTimeout(resolve))
        const wrapper = mount(global);
        const url = wrapper.vm.$data.url;
        const fakeData = {...some data...}

        mockAxios.onGet(url).reply(200, fakeData);

        await flushPromises();

        wrapper.vm.$nextTick(() => {
               expect(mockAxios.history.get[0].url).toEqual(url);
               expect(wrapper.vm.$data.data).toEqual(fakeData);
               done();
         })
    })
})

Actually, it seems to me i tried all of the methods in this discussion - i used axios strictly from package, i imported it from other file (now i use this method), I tried to reset it on each iteration - didn't help either:

describe('accessing to server', () => {
            let mockAxios;

            beforeAll(() => {
                mockAxios = new MockAdapter(axios)
            });

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

            afterAll(() => {
                mockAxios.restore()
            });
....

Even run yarn test and yarn run test
What am I doing wrong? I am using rails webpacker - not quite sure if it makes sense for current issue. All other non-mocking-axios tests are passing fine, problem is in running multiple mocking-axios tests.

@kylemh
Copy link

kylemh commented May 27, 2019

Aside from only exporting one mock instance, I've also been bitten in the butt when I use the spread operator in the body and the object contains extra fields than expected. Just wanted to share.

I really wish we'd see some error a la: Expected mock adapter to have PATH be requested with BODY_A, but was requested with BODY_B.

@ndraiman
Copy link

ndraiman commented Sep 26, 2019

I'm having the same error.

I tried the following:

adapter.onPost(`${baseUrl}/endpoint`).reply(config => {
    console.log('config', config);
    return [200];
});

This return the 404 error: Error: Request failed with status code 404

But dropping the url matcher works:

adapter.onPost().reply(config => {
    console.log('config', config);
    return [200];
});

Note that config.url matches the value of `${baseUrl}/endpoint`

rija pushed a commit to rija/gigadb-website that referenced this issue Jan 17, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
@sohymg
Copy link

sohymg commented Mar 11, 2020

I faced the same error and the solution from @vsakaria worked for me. Basically the payload wasn't matching that's why it will also work if you remove the payload from mock.onPost() like @s8sachin mentioned.

rija pushed a commit to rija/gigadb-website that referenced this issue Aug 22, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
rija pushed a commit to rija/gigadb-website that referenced this issue Sep 6, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
rija pushed a commit to rija/gigadb-website that referenced this issue Sep 19, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
rija pushed a commit to rija/gigadb-website that referenced this issue Sep 25, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
rija pushed a commit to rija/gigadb-website that referenced this issue Sep 29, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
@Jackman3005
Copy link

Jackman3005 commented Oct 28, 2020

I spent awhile on a bug today where we had accidentally passed in incorrect data in an onPost body and were receiving a 404 when our app code tried to make the call with the correct data... I found that the 404 was a difficult message to work with. Because this particular test had multiple API calls, I couldn't sort out which one it was immediately, and I had to fiddle with things until I noticed my mistake. In fact... I ended up console.logging every parameter to the utils.createAxiosError function and that's where I found our mistake...

Is there anyway that we could get more output for this situation? Like "Hi you sent me this payload, header, url, etc but that doesn't match anything you've set up for me." If being extra ambitious, we could show them the closes matches to the config MockAdapter received. I'd be happy to help with this, but I just naively felt like I want to put console logs in the utils.createAxiosError block, which clearly can't be the best approach (also noticing the toJSON() function definition there, how do you expect that to be used?)

Here is a code sample that illustrates the situation:

Test Setup Code

import axios from 'axios'
const mockAdapter = new MockAdapter(axios)
mockAdapter.onPost('/api/my-url', {
  something: [
    {
      i: "hope",
      that: "this",
      matches: "whoops-typo"
    }
  ]
}).replyOnce(200)

App Code

await axios
  .post('/api/my-url/', {
    something: [
      {
        i: "hope",
        that: "this",
        matches: "but-it-wont"
      }
    ]
  })

Resulting Message

Error: Request failed with status code 404

    at createAxiosError (/Users/hello/some-project/node_modules/axios-mock-adapter/src/utils.js:148:15)
    at Object.settle (/Users/hello/some-project/node_modules/axios-mock-adapter/src/utils.js:127:11)
    at handleRequest (/Users/hello/some-project/node_modules/axios-mock-adapter/src/handle_request.js:126:15)
    at /Users/hello/some-project/node_modules/axios-mock-adapter/src/index.js:26:9
    at new Promise (<anonymous>)
    at MockAdapter.<anonymous> (/Users/hello/some-project/node_modules/axios-mock-adapter/src/index.js:25:14)
    at dispatchRequest (/Users/hello/some-project/node_modules/axios/lib/core/dispatchRequest.js:59:10)

Potential Desired Message

Error: Request did not match any stubbed request and pass-through mode is turned off!

   ...
   at myAppMethod (/Users/..../myAppAxiosCall.js:44)
   ...

   The un-matched request: {
        method: 'POST',
        url: '/api/my-url',
        body: {
          something: [
             {
                i: "hope",
                that: "this",
                matches: "but-it-wont"
             },
          ]
        },
        headers: ...

Thank you for reading this far. I apologize if this is not the thread to have posted this request. I am happy to make a new issue. Again, I would consider helping out if I had the guidance for what you would prefer.

@ctimmerm
Copy link
Owner

@Jackman3005: The default behavior of the mock adapter is to return a 404 when a request can't be matched to a handler. The reason for this is that it's not possible to know whether the lack of a match was intentional or a mistake. You can change this to throw an exception when no match is found:

new MockAdapter(axiosInstance, { onNoMatch: "throwException" });

Could you check if that would work for you and if there's something we can still improve there?

@gwuah
Copy link

gwuah commented Oct 29, 2020

Weirdly enough, this was the fix to my problem. Alll I had to do was to pass { onNoMatch: "throwException" } to my mockAdapter initialization and the problem went away.
Today was the third time facing this issue. I don't even remember how i fixed it the previous times.

rija pushed a commit to rija/gigadb-website that referenced this issue Nov 7, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
rija pushed a commit to rija/gigadb-website that referenced this issue Nov 23, 2020
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
rija pushed a commit to rija/gigadb-website that referenced this issue Jan 19, 2021
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
@snamoah
Copy link

snamoah commented Oct 27, 2021

I was getting the same error Error: Request failed with status code 404. I fixed making sure to declare the mocked request before actually rendering the component that makes the axios request.

pli888 pushed a commit to pli888/gigadb-website that referenced this issue Nov 24, 2021
Normally we'd be mocking the web client library, but the two libraries I've tried
are not reliable.

axios-mock-adapter simply doesn't work with it succumbing to this issue:
ctimmerm/axios-mock-adapter#116 (comment)
Tried everything there, no success.

Moxios, is the one I've first chosen and used in previous commits. It's older,
not updated in years and more problematic is that it's prone to "heisenbugs", i.e:
randomly breaking for no obvious reason.

I haven't tried jasmine-ajax, but I figured it's much more time-effective and reliable
to fake the API instead (we are talking about unit tests here, so they should be isolated
from the real implementation). Bonus point is that we can use the fake API to test
and prototype API endpoints with sample data before implementing for real (and thus
being able to try multiple designs).

In the future, we should revisiting the async mocking libraries as that's the long-term proper practice.
@Gibbo3771
Copy link

Gibbo3771 commented Apr 12, 2022

Weirdly enough, this was the fix to my problem. Alll I had to do was to pass { onNoMatch: "throwException" } to my mockAdapter initialization

This is really frustrating. I was convinced that it was throwing a 404 because the url was fake. Is it not possible to log that a request was made and no match was found? If there is a way to detect a no match, then there is a way to soft fail right?

Add more context here? https://github.com/ctimmerm/axios-mock-adapter/blob/master/src/handle_request.js#L131

Or have the default behaviour as throw an exception stating no match was found. Would it not be better to fail early and give a contextual error?

@snowden-fu
Copy link

@djalmaaraujo I think its how you are using the base url. You can console.log(mock.handlers.get) to see if the right url is saved in the array.

I just mocked this up and it works just fine isolated from Redux.

  fit('it should work', () => {
    const SERVER_URL = '/test';
    const requestInstance = axios.create({
      baseURL: SERVER_URL,
      timeout: 2000,
      headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
    });

    const newMock = new MockAdapter(requestInstance);
    newMock.onGet('/api/quoteDetails/product/123456').reply(200, 'quoteDetailsData');

    requestInstance.get('api/quoteDetails/product/123456')
      .then(response => console.log('response', response))
      .catch(error => console.log('error', error));
  });

Response console.log

 response { status: 200,
      data: 'quoteDetailsData',
      headers: undefined,
      config:
       { adapter: null,
         transformRequest: { '0': [Function: transformRequest] },
         transformResponse: { '0': [Function: transformResponse] },
         timeout: 2000,
         xsrfCookieName: 'XSRF-TOKEN',
         xsrfHeaderName: 'X-XSRF-TOKEN',
         maxContentLength: -1,
         validateStatus: [Function: validateStatus],
         headers:
          { Accept: 'application/json',
            'Content-Type': 'application/json' },
         baseURL: '/test',
         method: 'get',
         url: '/api/quoteDetails/product/123456',
         data: undefined } }

yeah, the same using Redux, but why is it?

@snowden-fu
Copy link

interesting thing is If I call loginRequest, it won't work and say 404, but when I copy loginRequest to Jest it will work.

describe('when login', () => {
        it('should return auth token if success',
            async () => {
            const username:string = "fz"
                const role:string = 'host'
                const password:string = "123456"
                const user = {username,password,role}
                mock.onPost(BASE_URL + "/login", {

                    'Content-Type': 'application/json',
                    "Access-Control-Allow-Origin": BASE_URL

                }).reply(200, {'token':'123'})
// if calling loginRequest(), it won't work 
                axios.post("http://127.0.0.1:5000/login",
                    JSON.stringify(user),
                    {
                        headers:{
                            'Content-Type': 'application/json',
                            "Access-Control-Allow-Origin": BASE_URL
                        }
                    })
                expect(mock.history.post[0]["url"]).toEqual(BASE_URL + "/login");
                // console.log(mock.history.post[0].url)

                return
            })
export function loginRequest(data:{username:string, password:string, role:string}) {
    const d = JSON.stringify(data)
    console.log(d)
 return axios.post("http://127.0.0.1:5000/login",
  d,
  {
    headers:{
        'Content-Type': 'application/json',
        "Access-Control-Allow-Origin": BASE_URL
    }
  }
  )
}

@edwinwong90
Copy link

I was experiencing the same issue at my first try. I found out when use new MockAdapter(axios) all your external endpoint would return 404 even you didn't declare. You can try this

const mocker = new MockAdapter(axios);

// Declare your mock
mocker.onGet('/user').reply(200, {});

// this must be the last line! it tell any endpoint doesn't match will let it pass through
mocker.onAny().passThrough();

Hope it help!

@juburr
Copy link

juburr commented Nov 30, 2022

I haven't investigated too deeply yet, but axios-mock-adapter was working great for me on a very large project. After upgrading from webpack 4 to webpack 5 (with a few changes to babel for the polyfill changes), I suddenly started receiving 404's back in all of our unit tests. Almost everything else appears to be working, and the app still functions properly in a web browser.

@juburr
Copy link

juburr commented Jan 6, 2023

Just closing the loop on my previous comment in case if helps someone else who stumbles into this thread. My issue was that karma-webpack needed a few more changes to work properly with webpack 5. This wasn't at all apparent because the only symptom was the 404's coming from axios-mock-adapter. I conditionally added the following lines to my webpack config for unit tests only and then everything started working again:

optimization: {
    runtimeChunk: false,
    splitChunks: false
},

Magic!

@Uzwername
Copy link

Uzwername commented Jan 24, 2023

In my case the reasoning was pure fun & had nothing to do with axios-mock-adapter. Leaving it here just in case it helps someone.

I use axios-retry, so, when a request fails it retries it a couple of times. Great to have in prod, not so much in the testing environment.

I had axios-retry configured in a service, so I had something like:

// ../services/http.ts

import axios from 'axios';
import axiosRetry from 'axios-retry';

const http = axios.create({
  timeout: 1 * 1000 * 15,
});

axiosRetry(http, {
  retries: 3,
  retryDelay: axiosRetry.exponentialDelay,
});

export default http;

Then, in tests I (sure) forgot about this fact & tried to use:

// some.test.ts

const httpMock = new MockAdapter(http);

// ...

httpMock
  .onGet('...')
  .replyOnce(500);

After the first 500 reply, axios-retry retried the request the second time but (oops) we don't have any handler for this request registered anymore, so, the 404 error is issued.

I worked around that by mocking my http module out with something like this:

// some.test.ts
import axios from 'axios';

jest.mock('../services/http', () => require('axios'));

const axiosMock = new MockAdapter(axios);

// ...

axiosMock
  .onGet('...')
  .replyOnce(500);

@GauravChinavle
Copy link

import axios, { AxiosRequestConfig } from 'axios';
import MockAdapter from "axios-mock-adapter";
const mock = new MockAdapter(axios);

// Update the endpoint to match the actual application endpoint
mock.onPost("http://localhost:3110/mockApi").reply(200, {
    users: [{ id: 1, name: "John Smith" }],
});

const url = "http://localhost:3110/mockApi";

(async () => {
    const config: AxiosRequestConfig<any> = {
        method: 'POST',
        headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json'
        },
        data: {
            '$mock': 'TEST_DATA'
        }
    }
    try {
        const response: any = await axios(url, config);
        console.log("response->", response);
    } catch (error) {
        console.error("Error:", error);
    }
})();

Proposed Solution:

  1. Verify the application's actual endpoint and update it in the onPost function to match.
  2. Confirm that the application is running on the specified base URL and port, adjusting the URL accordingly.

I was able to intercept axios-mock-adapter onPost request

@ofirdagan
Copy link

I don't know if it's the same issue or not but my mock requests also failed w/ 404 status and the problem for me was that the axios instance added default headers such as Content-Type which I didn't check. I only checked my custom headers. So the solution for me was to change:

axiosMock
        .onPost(`${SERVER_URL}/bla`, requestPayload, {
          Authorization: AUTHORIZATION
        });

To:

axiosMock
        .onPost(`${SERVER_URL}/bla`, requestPayload, {
          Authorization: AUTHORIZATION
          Accept: 'application/json, text/plain, */*',
          'Content-Type': 'application/json',
        });

Hope that helps.

@shuweili1122
Copy link

pretty sure this happens with onPost mock, and realy have no clue why it happens... I saw some comments on the internet say it's because of the headers, but i tried to match the headers, and it doesn't work for me.

I also did try change the call itself from post to get, and change the mock from onPost to onGet, and everything works flawlessly with the same headers... so really have no idea why the author wouldn't fix it for onPost, or at least give some more meaningful error log...

Finally I do find a hacky workaround, just simply do it this way, and check your call's url and other stuff in the callback, and make sure it's the call u wanna test...

mockAdapter.onPost().reply(config => { if (config.url === 'your_url') { return [200, 'your response'] } });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests