ctimmerm / axios-mock-adapter

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

Axios catch error Request failed with status code 404 #193

Closed priyeshvadhiya closed 5 years ago

priyeshvadhiya commented 5 years ago

i am setting up unit test with jest in my vuejs project.

testing with login component and i have tested successful all the props, input, client validation, but when i test with axios request POST then always return catch error

Error: Request failed with status code 404

console.log node_modules/raven-js/src/console.js:35 { Error: Request failed with status code 404 at createErrorResponse (/Users/artixun/Priyesh/Vue/work/e3-unit-test/node_modules/axios-mock-adapter/src/utils.js:117:15) at Object.settle (/Users/artixun/Priyesh/Vue/work/e3-unit-test/node_modules/axios-mock-adapter/src/utils.js:97:16) at handleRequest (/Users/artixun/Priyesh/Vue/work/e3-unit-test/node_modules/axios-mock-adapter/src/handle_request.js:78:11) at /Users/artixun/Priyesh/Vue/work/e3-unit-test/node_modules/axios-mock-adapter/src/index.js:18:9 at new Promise (<anonymous>) at MockAdapter.<anonymous> (/Users/artixun/Priyesh/Vue/work/e3-unit-test/node_modules/axios-mock-adapter/src/index.js:17:14) at dispatchRequest (/Users/artixun/Priyesh/Vue/work/e3-unit-test/node_modules/axios/lib/core/dispatchRequest.js:59:10) at process._tickCallback (internal/process/next_tick.js:68: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, */*', Accepts: 'application/json', 'Access-Control-Allow-Origin': '*' },   

i am setting up unit test with jest in my vuejs project.

testing with login component and i have tested successful all the props, input, client validation, but when i test with axios request POST then always return catch error

Error: Request failed with status code 404

login.spec.js

import Vue from 'vue'
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Login from '../../src/components/global/login/Login.vue';
import Raven from "raven-js";
import jQuery from 'jquery'
import Vuex from 'vuex'
import router from '../../src/router'
var axios = require('axios');
var MockAdapter = require('axios-mock-adapter');

describe('Login.vue', () => {
let wrapper;
let componentInstance;
let mock;
beforeEach(() => {
    global.requestAnimationFrame = setImmediate,
    mock = new MockAdapter(axios)
    wrapper = shallowMount(Login, {
 router,
 $: jQuery,
 attachToDocument: true,
 mocks: {
   $t: () => { },
   Raven: Raven,
 },
data() {
      return {
         email: '',
        password: '',
      }
    }
  })
  componentInstance = wrapper.vm;
 })

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

my test code

 it('calls `axios()` with `endpoint`, `method` and `body`',async () => {
   const formData = {
     email: 'example@gmail.com',
     password: '111111'
   };

  let fackData = {"fack response"}
  mock.onPost(`${process.env.VUE_APP_BASE_URL}/login/`, 
  formData).reply(200, fackData);

   wrapper.vm.email = 'priyesh.vadhiya@gmail.com';
   wrapper.vm.password = 'aaaaaaa';
   wrapper.vm.doSigninNormal()
  });

Login.vue

 doSigninNormal() {
  const formData = {
  email: this.email,
  password: this.password
   };
   this.$v.$touch()
   if(this.$v.$invalid ){
        this.loading = false;
        this.emailLostFocus = true;
        this.passwordLostFocus = true;
        $('html, body').animate({scrollTop:110}, 'slow')
    }
   else{
     axios
        .post("/login", formData, {
           headers: { "X-localization": localStorage.getItem("lan") }
        })
        .then(res => {
         console.log('then',res);
         if (!res.data.result) {
               if (res.data.errors) {
                   for (var i = 0; i < res.data.errors.length; i++) {
                       this.$toaster.error(res.data.errors[i].message);
               if (
                     res.data.errors[0].message == "Your email is not yet verified"
                 ) {
                  this.showVerificationLinkButton = true;
                  }
                if (res.data.errors[i].field === "email") {
                    this.$toaster.error(res.data.errors[i].message);
                 }
           if (res.data.errors[i].field === "password") {
            this.$toaster.error(res.data.errors[i].message);
            }
           }
        }

        this.loading = false;
         this.$v.$reset();
         } else {
        this.loading = false;
       Raven.setUserContext({
          email: res.data.user.email,
           id: res.data.user.id
         });
       this.$store.dispatch("login", res);
      this.$v.$reset();
      }
    })
     .catch((err) => {
         console.log('catch',err);
     });

i am trying to lot. how to moke axios by calling method? positive answer appreciate

i want to call function and then call api(inside the function) and get response. wrapper.vm.doSigninNormal()

i have also post on stackOverflow to describe it.

Tryliom commented 5 years ago

Hi, I have the same issue, here I have a post on StackOverflow to describe it and I think it's probably a real issue and not just a problem with us.

Tryliom commented 5 years ago

There is a sandbox with an example of the issue. There is just a App.js that display the text "App", we call a random link that if success it change the text to "NewApp". In the App.test.js we shallow the App and test before if text is "App", and after we let axios call go and the mockup doesn't affect it and return an error (So the test fail).

gogoyqj commented 5 years ago

same issue, out of date?

Tryliom commented 5 years ago

@gogoyqj We are always searching for a fix, it seems to be a real issue in axios-mock-adapter

haroonKhan-10p commented 5 years ago

The issue is on the axios-mock-adapter package. It requires an instance of axios using the .create() method. See here: creating an instance

In your App.js, use:

import axios from "axios";
const instance = axios.create();

instance.post("http://localhost/api/user/update", {name: "Test"}, {headers: {"Authorization": "Bearer token")}});

Nothing needs to be changed in the tests though.

I got the hint from tests of axios-mock-adapter.

An example of such is: post test

Tryliom commented 5 years ago

@haroonKhan-10p I have test what you propose on my CodeSandbox and it result in the same error. Have you a code that we can test like mine ? Thanks.

mattcarlotta commented 5 years ago

Including the headers within the onPost, onGet, onPut, and onDelete mocked function will cause the function to throw a Error: Request failed with status code 404. While this may be considered a bug, it's really not needed for mocked requests.

Since you're just mocking requests, and not actually testing an integration with an API, you don't need to include the headers in the mocked request -- even if you did include them, there's no point, as the client doesn't care if they're valid and/or present, only the API does.

If you wanted to test that the headers are valid/present, then you wouldn't mock axios, and instead you would write an Integration (End to End) test with your API. Or you could just write an implementation test on your API's route controller and make assertions against valid/invalid requests.

On that note, if you're already using a custom axios configuration, then you can just include the headers in the configuration file and then mock that instance (again, still pointless for client-side implementation testing):

import axios from "axios";

const app = axios.create({
  baseURL: "http://example.com/api/",
  headers: {
    Authorization: "Bearer token",
    X-localization: localStorage.getItem("lan"),
   ...etc
  }
});

export default app;

See examples here: https://codesandbox.io/s/526pj28n1n

priyeshvadhiya commented 5 years ago

see this stackOverflow to describe it

cjmoran-vw commented 5 years ago

Including the headers within the onPost, onGet, onPut, and onDelete mocked function will cause the function to throw a Error: Request failed with status code 404. While this may be considered a bug, it's really not needed for mocked requests.

This was my first time setting this library up, and I had no idea why my mocks kept erroring out with a 404. If you don't support headers (which is ridiculous IMO, there are plenty of good reasons to test header management and such) then you should be printing out a useful error message saying so, not a mystery 404.

Sorry if I'm being a little harsh, but this wasted like 2 hours of my day before I finally stumbled across this issue buried in your GitHub.

mattcarlotta commented 5 years ago

If you don't support headers (which is ridiculous IMO, there are plenty of good reasons to test header management and such) then you should be printing out a useful error message saying so, not a mystery 404.

While I'm not a contributor/collaborator for this package, implementing such a feature would be pointless as the client doesn't care whether or not the headers are there... only the API does. If you want to test header management, your tests should be end-to-end instead of unit.

For example, lets say you have an isomorphic application that sends a login request to your API. The API sees the request and sends back a response with a unique cookie. On each successive request that cookie is sent to the API to ensure the user is still authenticated. As far as the client is concerned, it's job is to just send requests and receive responses regardless of whether or not that cookie is present.

On the other hand, the API cares that the request contains the correct URL, method, parameters, AND contains this unique cookie and then responds accordingly. Therefore adding something that is API-centric on the client-side doesn't make much sense.

As mentioned above, you can add headers to a custom axios configuration, but it's still pointless for testing purposes as it's never being tested against on the client-side. Meaning, the client won't authenticate a request if the headers are invalid (at least it shouldn't in practical terms). Instead, that's your API's job. The mocked request's purpose is to mimic an API response and it doesn't needs headers to do so.

snowden-fu commented 2 years ago

I have set instance as said above, but it still says Error: Request failed with status code 404, any hint for my problem

      describe('when login', () => {
let mock:MockAdapter;
    let instance:AxiosInstance;
    beforeAll(() => {
        instance = axios.create();
        mock = new MockAdapter(instance);
      });
        it('should return auth token if success',
            async () => {

                const username = "fz"
                const role = 'host'
                const password = "123456"
                const user={
                    username,
                    role,
                    password
                }
                mock.onPost("http://127.0.0.1:5000/login", JSON.stringify(user), {
                    'Accept':"'application/json",

                    'Content-Type': 'application/json',

                }).reply(200)
                return await instance.post("http://127.0.0.1:5000/login",
                    {username, password, role},
                    {
                        headers: {
                            'Content-Type': 'application/json',
                            "Access-Control-Allow-Origin": BASE_URL
                        }
                    }
                ).then(
                    (r)=>{
                        expect(r.status).toEqual(200);
                    }
                )
                // console.log(mock.history.post[0].url)

            })
       })