ctimmerm / axios-mock-adapter

Axios adapter that allows to easily mock requests
MIT License
3.42k stars 241 forks source link

Mock failed with 404 Error #116

Open kirankbs opened 6 years ago

kirankbs commented 6 years ago

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 commented 3 years ago

@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 commented 3 years ago

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.

snamoah commented 2 years ago

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.

Gibbo3771 commented 2 years ago

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 commented 1 year ago

@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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 9 months ago
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 commented 7 months ago

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 commented 5 months ago

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'] } });