Skip to content

Latest commit

 

History

History
182 lines (140 loc) · 7.07 KB

File metadata and controls

182 lines (140 loc) · 7.07 KB

六、测试方法和模拟相关性

我们应该在方法上测试什么?这是我们开始进行单元测试时遇到的一个问题。一切都归结为测试该方法的功能,只有。这意味着我们需要避免调用任何依赖项,因此我们需要模拟它们。

让我们在上一章中创建的Form.vue组件的表单中添加一个submit事件:

<form @submit.prevent="onSubmit(inputValue)"></form>

.prevent修饰符只是调用event.preventDefault()的一种方便方式,这样它就不会重新加载页面。现在,进行一些修改以调用 API,然后通过向数据中添加一个results数组和一个onSubmit方法来存储结果:

export default {
  data: () => ({
    inputValue: "",
    results: []
  }),
  methods: {
    onSubmit(value) {
      axios
        .get("https://jsonplaceholder.typicode.com/posts?q=" + value)
        .then(results => {
          this.results = results.data;
        });
    }
  }
};

这里,该方法是使用axiosjsonplaceholderposts端点执行 HTTP 调用,这只是此类示例的 RESTful API。此外,通过q查询参数,我们可以使用提供的value作为参数来搜索帖子。

对于onSubmit方法的测试:

  • 我们不想调用axios.get实际方法。
  • 我们想检查它是否正在调用 axios(但不是真正的 axios),以及它是否返回了一个promise
  • promise回调应该将this.results设置为承诺的结果。

当您有外部依赖项,再加上那些在内部做事情的返回承诺时,这可能是最难测试的事情之一。我们需要做的是模拟外部依赖关系

模拟外部模块依赖关系

Jest 提供了一个非常好的模拟系统,它允许您以非常方便的方式模拟所有内容。事实上,您不需要任何额外的库来完成这项工作。我们已经看到了jest.spyOnjest.fn用于监视和创建存根函数,尽管这对于本例来说还不够。

在这里,我们需要模拟整个axios模块。这就是jest.mock发挥作用的地方。通过在文件顶部写入以下内容,我们可以轻松模拟模块依赖关系:

jest.mock("dependency-path", implementationFunction);

您必须知道jest.mock被提升,这意味着它将被放置在顶部:

jest.mock("something", jest.fn);
import foo from "bar";
// ...

因此,前面的代码相当于:

import foo from "bar";
jest.mock("something", jest.fn); // this will end up above all imports and everything
// ...

在写这篇文章的时候,我还没有找到很多关于如何在互联网上开玩笑的信息。幸运的是,你不必经历同样的挣扎。

让我们在Form.test.js测试文件的顶部为axios编写模拟,并编写相应的测试用例:

jest.mock("axios", () => ({
  get: jest.fn()
}));
import { shallowMount } from "@vue/test-utils";
import Form from "../src/components/Form";
import axios from "axios"; // axios here is the mock from above!
// ...
it("Calls axios.get", () => {
  cmp.vm.onSubmit("an");
  expect(axios.get).toBeCalledWith(
    "https://jsonplaceholder.typicode.com/posts?q=an"
  );
});

这太棒了。我们确实在模仿axios,所以没有调用原始 axios,也没有调用任何 HTTP。我们甚至通过使用toBeCalledWith来检查它是否使用了正确的参数调用。然而,我们仍然缺少一些东西:我们没有检查它是否返回promise

首先,我们需要使 mockaxios.get方法返回一个promisejest.fn接受工厂函数作为参数,我们可以用它来定义它的实现:

jest.mock("axios", () => ({
  get: jest.fn(() => Promise.resolve({ data: 3 }))
}));

但是,我们仍然无法访问promise,因为我们不会返回它。在测试中,在可能的情况下从函数返回某些内容是一种很好的做法,因为这会使测试更加容易。那么,现在让我们在Form.vue组件的onSubmit方法中执行此操作:

export default {
  methods: {
    // ...
    onSubmit(value) {
      const getPromise = axios.get(
        "https://jsonplaceholder.typicode.com/posts?q=" + value
      );
      getPromise.then(results => {
        this.results = results.data;
      });
      return getPromise;
    }
  }
};

然后,我们可以在测试中使用非常干净的 ES2017async/await语法来检查承诺结果:

it("Calls axios.get and checks promise result", async () => {
  const result = await cmp.vm.onSubmit("an");
  expect(result).toEqual({ data: [3] });
  expect(cmp.vm.results).toEqual([3]);
  expect(axios.get).toBeCalledWith(
    "https://jsonplaceholder.typicode.com/posts?q=an"
  );
});

在这里,您可以看到,我们不仅检查承诺的结果,而且还按照预期,通过执行expect(cmp.vm.results).toEqual([3])更新组件的results内部状态。

保持模拟的外部化

Jest 允许我们通过将所有模拟放在__mocks__文件夹下并保持测试尽可能干净,将它们分离到自己的 JavaScript 文件中。

因此,我们可以将Form.test.js文件顶部的jest.mock...块取出到它自己的文件中:

// test/__mocks__/axios.js
module.exports = {
  get: jest.fn(() => Promise.resolve({ data: [3] }))
};

就像这样,Jest 不需要额外的努力就可以在我们所有的测试中自动应用模拟,这样我们就不需要做任何额外的事情,或者在每个测试中手动模拟它。请注意,模块名称必须与文件名匹配。如果再次运行测试,它们仍应通过。

请记住,模块注册表和模拟状态保持原样,因此,如果您在之后编写另一个测试,可能会得到不希望的结果:

it("Calls axios.get", async () => {
  const result = await cmp.vm.onSubmit("an");
  expect(result).toEqual({ data: [3] });
  expect(cmp.vm.results).toEqual([3]);
  expect(axios.get).toBeCalledWith(
    "https://jsonplaceholder.typicode.com/posts?q=an"
  );
});
it("Axios should not be called here", () => {
  expect(axios.get).toBeCalledWith(
    "https://jsonplaceholder.typicode.com/posts?q=an"
  );
});

第二次测试应该失败,但是没有。那是因为axios.get之前在测试中被调用过。

出于这个原因,清理模块注册表和 mock 是一个很好的实践,因为它们是由 Jest 操纵的,以便进行 mock。为此,您可以添加您的beforeEach

beforeEach(() => {
  cmp = shallowMount(Form);
  jest.resetModules();
  jest.clearAllMocks();
});

这将确保每个测试都从干净的模拟和模块开始,就像在单元测试中一样。

收工

Jest 的模拟功能和快照测试是我最喜欢的两件事。这是因为它们使通常很难测试的东西变得非常容易,允许您专注于编写更快、更好的独立测试,并保持代码库的防弹性。

您可以在GitHub上找到本章的所有代码 https://github.com/alexjoverm/vue-testing-series/tree/Test-State-Computed-Properties-and-Methods-in-Vue-js-Components-with-Jest )。