学习编写测试用例

1,488 阅读4分钟

在自己写组件时,验证组件是否可以使用,通常是放在页面当中,但是这样比较繁琐,需要自己用鼠标一个去触发,效率很低。如果需求有一点改动,那么组件还能不能正常运行,这是一个未知的事情。

为保证组件质量,就需要引入单元测试,当需求改变时,单元测试可能就编译不通过,就能第一时间修改bug,保证组件质量。

单元测试

单元测试一般分为两种BDDTDD

  • BDD:行为驱动开发,Behavior-driven development
  • TDD:测试驱动开发,Test-driven development

行为驱动开发是用来模拟用户行为,用自然语言描述需求;测试驱动开发是完成产品需求;

单元测试中有一个概念Assert,中文意思是断言,就是说我主观臆断认为是对的,如果断言正确,啥事也不会发生,否则会报错。

安装

这里使用的工具是:

  • Karma是一个测试运行器,它可以呼起浏览器,加载测试脚本,然后运行测试用例
  • Mocha是一个单元测试框架/库,它可以用来写测试用例
  • Sinon是一个spy/stub/mock库,用以辅助测试

安装相关插件:

npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies

在根目录下新建一个karma.conf.js,其中files中需要将css也填进来,否则样式这块无法测试。

module.exports = function (config) {
  config.set({
    basePath: "",
    frameworks: ["mocha", "sinon-chai"],
    client: {
      chai: {
        includeStack: true,
      },
    },
    files: ["dist/**/*.test.js", "dist/**/*.test.css"],
    exclude: [],
    preprocessors: {},
    reporters: ["progress"],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ["ChromeHeadless"],
    singleRun: false,
    concurrency: Infinity,
  });
};

编写单元测试

组件单元测试编写分为 4 个部分:

  • 页面中组件是否存在
  • 组件的属性是否设置正确
  • 组件的样式是否加载正确
  • 组件的事件是否触发

准备工作

在编写测试用例之前,引入相关依赖

const expect = chai.expect;
import Vue from "vue";
import Button from "../src/button";

单元测试中的几个概念

describe是一个用例测试集,可以进行嵌套

describe("Button", () => {
  // 进行 Button 相关的单元测试
});

一个it对应一个单元测试用例

it("", () => {
  // 相关测试代码
});

onlyskip

  • only在当前的父 describe 块下,只执行该单元的测试
  • skip在当前的父 describe 块下,跳过该单元的测试
describe("Button", () => {
  describe.only("父describe块下只执行该测试单元", () => {
    it.skip("跳过的测试单元", () => {});
  });
});

页面中组件是否存在

it("页面中组件是否存在", () => {
  except(Button).to.be.ok; // 检测页面中是否有组件
});

组件的属性是否设置正确

  • Button组件属性icon,是用来设置Button中的图标
  • Button组件属性loading,是用来设置Button中的加载图标,它和icon只能出现一个。

通过这个用例分析,可以知道这里要写两个测试用例。

用例 1:

it("可以设置 icon", () => {
  const Constructor = Vue.extend(Button);
  const vm = new Constructor({
    propsData: {
      icon: "settings",
    },
  }).$mount(); // 挂载,在内存中
  const useElement = vm.$el.querySelector("use"); // 获取到 use 元素
  expect(useElement.getAttribute("xlink:href")).to.eq("#i-settings"); // 获取 use 元素的属性,用断言检测 xlink:href 是否为 #i-settings
  vm.$destroy();
});

用例 2:

it("设置 loading", () => {
  const Constructor = Vue.extend(Button);
  const vm = new Constructor({
    propsData: {
      loading: true,
      icon: "settings",
    },
  }).$mount();
  const useElement = vm.$el.querySelectorAll("use"); // 获取所有 use 元素
  expect(useElement.length).to.equal(1); // 检测 use 元素的数量等不等于 1,因为 loading 和 settings 只能出现一个
  expect(useElement[0].getAttribute("xlink:href")).to.eq("#i-loading"); // 获取 use 元素的属性,用断言检测 xlink:href 是否为 #i-loading
  vm.$destroy();
});

组件的样式是否加载正确

icon可能会出现在文字的左边或者右边,组件中我们是通过css去控制的,在编写测试用例时,就要检测样式是否正确。

  • icon默认样式的order1
  • 当设置了iconPosition后,iconorder2

样式测试,需要挂载到页面中才可以,这里编写测试用例,需要将组件挂载到页面中

用例 1:

it("icon 默认 order 为 1", () => {
  const div = document.createElement("div");
  document.body.appendChild(div);
  const Constructor = Vue.extend(Button);
  const vm = new Constructor({
    propsData: {
      icon: "setting",
    },
  }).$mount(div); // 挂载到页面中
  const svgElement = vm.$el.querySelector("svg");
  expect(getComputedStyle(svgElement).order).to.eq("1"); // 检测 svg 元素的样式 order 默认值是不是 1
  vm.$el.remove(); // 挂载到页面中的元素,需要被销毁
  vm.$destroy();
});

用例 2:

it("设置 iconPosition 可以改变 order", () => {
  const div = document.createElement("div");
  document.body.appendChild(div);
  const Constructor = Vue.extend(Button);
  const vm = new Constructor({
    propsData: {
      icon: "settings",
      iconPosition: "right", // 设置 iconPosition 为 right
    },
  }).$mount(div);
  const svgElement = vm.$el.querySelector("svg");
  expect(getComputedStyle(svgElement).order).to.eq("2"); // 设置了 iconPosition 为 right 后,检测 svg 元素的样式 order 默认值是不是 1
  vm.$el.remove();
  vm.$destroy();
});

组件的事件是否触发

it("点击 button 触发 click 事件", () => {
  const Constructor = Vue.extend(Button);
  const vm = new Constructor({
    propsData: {
      icon: "settings",
    },
  }).$mount();
  const callback = sinon.fake(); // 用 sinon 创建一个函数
  vm.$on("click", callback); // 绑定事件
  vm.$el.click(); // 点击调用
  expect(callback).to.have.been.called; // 检测点击时是否被调用
  vm.$destroy();
});

另外可添加微信ttxbg180218交流