前端jest单元测试

一、环境安装

vue cli3 安装命令:
vue add @vue/cli-plugin-unit-jest
配置文件 Jest.config.js
用例执行文件*.spec.js *.test.js
运行命令:
yarn test:unit

    "test:unit": "vue-cli-service test:unit ",
    "test:unit-w": "vue-cli-service test:unit --watchAll",
    "test:unit-u": "vue-cli-service test:unit --u",

二、使用方法

基础介绍

  1. Describe 用例分组
  2. It/test 用例执行方法
  3. expect().to** 匹配器,用于判断用例结果与预期结果是否相符
describe("分组描述2", () => {
  it("toBe/toEqual", () => {
    const a = { number: "007" };
    expect(a).toBe(a);
    expect(a).toEqual({ number: "007" });
  });
});

常用匹配器

  1. toBe 严格匹配器 === expect(a).toBe({ age: ‘007’ })=>false
  2. toEqual 不严格匹配但要求值相等 expect(a).toEqual({ age: ‘007’ })=>true
  3. toBeNull 只匹配 null 不匹配 undeifined
  4. toBeUndifined toBeDefined
  5. toBeTruthy toBeFalsy
  6. toBeGreaterThan 大于 toBeGreaterThanOrEqual() 大于等于
  7. toBeLessThan 小于 toBeLessThanOrEqual()匹配器 小于等于
  8. toBeCloseTo 清除浮点精度错误的匹配器 expect(0.1 + 0.2).toBeCloseTo(0.3)
  9. toMatch 字符串包含匹配器 toContain 数组包含匹配器
  10. toThrow 检测一个方法是否抛出异常
  11. 官方文档:https://jestjs.io/docs/en/expect

钩子函数

  1. beforeAll()钩子函数 在所有测试用例之前进行执行
  2. afterAll() 钩子函数是 在所有测试用例完成之后才执行的函数
  3. beforeEach()钩子函数,在每个测试用例前都会执行一次的钩子函数
  4. afterEach()钩子函数, 在每次测试用例完成之后执行一次的钩子函数

分组作用域

  1. 钩子函数在父级分组可作用域子集,类似继承
  2. 钩子函数同级分组作用域互不干扰,各起作用
  3. 先执行外部的钩子函数,再执行内部的钩子函数

Mock 数据

  1. mockReturnValueOnce 设置一次函数的返回值
  2. mockImplementationOnce 设置一次可以在函数里面写更多的逻辑
  3. mockReturnValue mockImplementation 设置每次返回值
it(" 设置mockReturnValueOnce返回值", () => {
  let fun = jest.fn();
  fun.mockReturnValueOnce("123"); // 设置调用函数1次的返回值,设置几个就是几次
  fun.mockReturnValueOnce("331");
  fun.mockReturnValueOnce("33");
  fun.mockReturnValue("666"); //设置每次调用函数的值都为666
  expect(callbackFun(fun)).toBe("123");
  expect(callbackFun(fun)).toBe("331");
  expect(callbackFun(fun)).toBe("33");
  expect(callbackFun(fun)).toBe("666");
});
it(" 设置mockImplementationOnce返回值", () => {
  let fun = jest.fn();
  // 设置调用函数1次的返回值
  fun.mockImplementationOnce(() => {
    return "123";
  });
  //设置每次调用函数的值都为666
  fun.mockImplementation(() => {
    return "666";
  });
  expect(callbackFun(fun)).toBe("123");
  expect(callbackFun(fun)).toBe("666");
});

Mock 接口数据

jest.mock(‘axios’)模拟 axios 请求数据,不会发送真实请求(改变了 axios 函数内部实现) 不会真的去请求后台接口数据,使用模拟数据

import axios from "axios";
jest.mock("axios");
describe("axios", () => {
  it("测试 getData,使用mock", async () => {
    // 模拟第一次接收到的数据
    axios.get.mockResolvedValueOnce({
      data: { num: "123" },
    });
    // 模拟每一次接收到的数据
    axios.get.mockResolvedValue({
      data: "456",
    });

    const data1 = await getData();
    const data2 = await getData();
    console.log(data1);
    expect(data1).toEqual({ num: "123" });
    expect(data2).toBe("456");
  });
});

异步

import axios from "axios";
function featchData2() {
  return axios.get(
    "https://bird.ioliu.cn/v2/?url=https://music.163.com/store/api/searchsuggest/get"
  );
}
describe("异步", () => {
  //错误示例
  it("测试 featchData2", () => {
    featchData2().then((res) => {
      //测试data中是否包含code: 200
      expect(res.data).toMatchObject({
        code: 200,
      });
    });
  });
  // 当你使用done时,测试用例会一直等到执行到done()方法才结束
  it("测试 1-featchData2", (done) => {
    featchData2().then((res) => {
      expect(res.data).toMatchObject({
        code: 200,
      });
      done();
    });
  });
  // // 如果返回的是一个 Promise 对象,可以直接使用 return 写法
  test("测试 2-featchData2", () => {
    return featchData2().then((res) => {
      expect(res.data).toMatchObject({
        code: 200,
      });
    });
  });
  // 如果返回的是一个 Promise 对象,可以直接使用 return + resolves/rejects 写法
  test("测试 3-featchData2", () => {
    return expect(featchData2()).resolves.toMatchObject({
      data: {
        code: 200,
      },
    });
  });
  // 第四种:如果返回的是一个 Promise 对象,async + await
  test("测试 4-featchData2", async () => {
    const results = await featchData2();
    expect(results.data).toMatchObject({
      code: 200,
    });
  });
});

Snapshot 快照测试

Snapshot 快照作用:把每次修改后的代码形成快照,与上次代码生成的快照做对比,若有修改部分,则会出现异常快照提示。

通过 yarn test:unit –u 更新快照

const generateConfig = () => {
  return {
    server: "http://localhost",
    port: 1111,
    domain: "localhost",
  };
};

it("测试 generateConfig ", () => {
  expect(generateConfig()).toMatchSnapshot();
});

行内 Snapshot

toMatchInlineSnapshot() 行内 Snapshot 把生成的快照放到了测试用例里边,作为第二个参数的形式传入。

toMatchSnapshot() 普通 Snapshot 会把生成的快照放到单独的文件里。

const generateAnotherConfig = () => {
  return {
    server: "http://localhost",
    port: 80801111,
    domain: "localhost",
    time: new Date(),
  };
};

test("测试 generateAnotherConfig ", () => {
  expect(generateAnotherConfig()).toMatchInlineSnapshot(
    {
      time: expect.any(Date),
    },
    `
        Object {
          "domain": "localhost",
          "port": 80801111,
          "server": "http://localhost",
          "time": Any<Date>,
        &#125;
    `
  );
&#125;);

Vue 组件

import navigationBar from "../../packages/navigation-bar/index.vue";
import &#123; mount &#125; from "@vue/test-utils"; //创建一个包含被挂载和渲染的 Vue 组件的 Wrapper
describe("vue", () => &#123;
  it("navbar", () => &#123;
    const wrapper = mount(navigationBar, &#123;
      // 通过 mount 生成了一个包裹器,包括了一个挂载组件或 vnode,以及测试该组件或 vnode 的方法
      // 可以带参数, propsData 传递了接口数据
      propsData: &#123;
        statusBarHeight: 45,
        navigationBarHeight: 30,
      &#125;,
      slots: &#123;
        default: "<span>我家云</span>",
      &#125;,
    &#125;);
    expect(wrapper.find("span").text()).toEqual("我家云");
  &#125;);
&#125;);