Axios 捕获错误请求失败,状态码 404

新手上路,请多包涵

我正在测试一个使用 Axios 的登录组件。我尝试使用 axios-mock-adapter Axios,但是当我运行测试时,它仍然会出错:

 Error: Request failed with status code 404

如何在测试中正确模拟 Axios?

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()
  })

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

    let fakeData = { data: "fake response" }
    mock.onPost(`${process.env.VUE_APP_BASE_URL}/login/`, formData).reply(200, fakeData);

    wrapper.vm.email = 'example@gmail.com';
    wrapper.vm.password = '111111';
    wrapper.vm.doSigninNormal()
  })
})

登录.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 => {
      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);
    });
  }
}

原文由 priyeshvadhiya 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.6k
2 个回答

测试错误的登录 URL

根本问题是测试代码设置 axios-mock-adapter 在与 Login.vue 中实际使用的不同的 URL 上,因此请求没有被存根:

 // login.spec.js:
mock.onPost(`${process.env.VUE_APP_BASE_URL}/login/`, formData).reply(200, fakeData)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// Login.vue
axios.post("/login", formData)
            ^^^^^^

解决方法是让测试代码使用相同的 URL(即 /login ):

 // login.spec.js
mock.onPost("/login", formData).reply(200, fakeData)

需要等待 axios.post()

单元测试不等待 POST 请求,因此测试将无法可靠地验证调用或响应(没有黑客攻击)。

修复方法是更新 doSigninNormal() 以返回 axios.post() 承诺允许调用者等待结果:

 // Login.vue
doSigninNormal() {
  return axios.post(...)
}

// login.spec.js
await wrapper.vm.doSigninNormal()
expect(mock.history.post.length).toBe(1)

验证登录结果

要验证结果,您可以声明一个本地数据属性来保存登录结果 1️⃣,更新 doSigninNormal() 以处理响应(在测试中用 fakeData ),捕获结果2️⃣。然后,在等待 doSignInNormal() 之后检查该数据属性。

 // Login.vue
data() {
  return {
    ...
    result: '' 1️⃣
  }
}
methods: {
  doSignInNormal() {
    return axios.post(...)
            .then(resp => this.result = resp.data.result) 2️⃣
  }
}

// login.spec.js
const result = await wrapper.vm.doSigninNormal()
expect(result).toBe(fakeData.result)
expect(wrapper.vm.result).toBe(fakeData.result)

使用 axios-mock-adapter 编辑模拟 Axios 调用

原文由 tony19 发布,翻译遵循 CC BY-SA 4.0 许可协议

模拟 Axios:

有两种简单的方法可以模拟 axios,这样您的测试就不会执行真正的 http 请求,而是使用模拟对象:

将 axios 设置为组件属性:

 import axios from 'axios`;
Vue.component({
  data() {
    return {
      axios,
    }
  },
  methods: {
    makeApiCall() {
      return this.axios.post(...)
    }
  }
})

因此,您可以轻松地在测试中注入模拟:


it('test axions', function() {
  const post = jest.fn();
  const mock = {
    post,
  }
  // given
  const wrapper = shallowMount(myComponent, {
    data: {
      axios: mock,
    }
  });

  // when
  wrapper.vm.makeApiCall();

  // then
  expect(post).toHaveBeenCalled();
});

我认为这是最直接的方法。

使用插件在每个组件中注入 axios

你可以设置一个像 vue-plugin-axios 这样的插件来自动将 axios 注入到每个组件中,比如:

   makeApiCall(){
    this.$axios.post(...)
  }

无需在 data 中明确声明。

然后在您的测试中,不要将模拟作为 data 的一部分传递,而是将其作为 mocks 的一部分传递,这就是 vue-test-utils 处理全局注入的方式--- :

 it('test axions', function() {
  const post = jest.fn();
  const mock = {
    post,
  }
  // given
  const wrapper = shallowMount(myComponent, {
    mocks: {
      $axios: mock,
    }
  });

  // when
  wrapper.vm.makeApiCall();

  // then
  expect(post).toHaveBeenCalled();
});

这是如何模拟 axios 调用以防止调用真正的 axios 并执行真正的 http 请求。

配置模拟行为和访问调用参数

使用 jest.fn 你可以设置一个模拟函数来返回一个特定的对象,比如:

const post = jest.fn( () => ({status: 200, response: ...}) )

您还可以通过 hasBeenCalledWith' method, or more complex stuff via mock.calls`( 更多信息在这里)访问调用的参数:

expect(post).toHaveBeenCalledWith(expectedParams)

因此,我认为您的最终测试应该如下所示:

 it('calls axios() with endpoint, method and body',async (done) => {

  // given
  const formData = { email: 'example@gmail.com', password: '111111' };
  const fakeResponse = {response: "fake response"};
  const email = 'example@gmail.com';
  const uri = 'somepath/login/'; // I dont think you can access Vue process env variables in the tests, so you'll need to hardcode.
  const password = '11111';

  const post = jest.fn(() => Promise.resolve({status: 200}) );

  const mock = {
    post,
  }
  const wrapper = shallowMount(Component, {
    data() {
      return {
        axios: mock,
        // email,
        // password, // you could do this instead to write to wrapper.vm later
      }
    }
  });
  wrapper.vm.email = 'example@gmail.com';
  wrapper.vm.password = '111111';

  // when
  await wrapper.vm.doSigninNormal();

  // then
  expect(post).toHaveBeenCalledWith({uri, password, email});

  // or
  const calls = post.mock.calls;
  const firstParam = calls[0][0];

  expect(firstParam.uri).toBe(uri);
  expect(firstParam.email).toBe(email);
  expect(firstParam.password).toBe(password);

  done();

});

原文由 Sergeon 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏