最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基于jest的前端自动化测试

    正文概述 掘金(controZL)   2020-12-18   586

    什么是自动化测试

    自动化测试是把以人为驱动的测试行为转化为程序执行的一种过程。通常,在需求确定以后,测试会编写测试用例,然后,根据测试用例中描述的规程一步步执行测试,得到实际结果与期望结果的比较。在此过程中,为了节省人力、时间或硬件资源,提高测试效率,便引入了自动化测试的概念。

    前端自动化测试

    高效的测试方法可以加快开发速度,提高代码质量,尽早发现并去除代码中的 BUG,前端实现自动化测试。通常有两种方案可以实现前端自动化测试

    • unit-test(单元测试):对应测试中的白盒测试。站在程序员的角度,在测试框架中执行代码,并且给出预期输出,然后对实际输出和预期输出进行比较。以此来判断是否通过测试。
    • e2e-test(端到端测试):对应测试中的黑盒测试。站在用户的角度,测试框架会模拟用户的行为,例如点击,输入等操作。然后观察页面中的元素展现形式是否和预期一致。以此来判断是否通过测试

    两者的存在的意义

    • unit-test(`单元测试)是程序员写好自己的逻辑后可以很容易的测试自己的逻辑返回的是不是都正确。
    • e2e-test(端到端测试)是测试所有的需求是不是都可以正确的完成,而且最重要的是在代码重构,js 改动很多之后,对需求进行测试的时候测试代码是不需要改变的,你也不用担心在重构后不能达到的需求。

    测试工具

    前端测试工具也和前端的框架一样纷繁复杂,其中常见的测试工具,大致可分为测试框架、断言库、测试覆盖率工具等几类。我们先来大致了解下它们:

    测试框架

    测试框架的作用是提供一些方便的语法来描述测试用例,以及对用例进行分组。 测试框架可分为两种: TDD (测试驱动开发)和 BDD (行为驱动开发) 常见的测试框架有 Jasmine, Mocha 以及本次分享要介绍的 Jest

    断言库

    断言库主要提供语义化方法,用于对参与测试的值做各种各样的判断。这些语义化方法会返回测试的结果,要么成功、要么失败。常见的断言库有 Should.js, Chai.js 等。

    测试覆盖率工具

    用于统计测试用例对代码的测试情况,生成相应的报表,比如 istanbul

    为什么选择 Jest

    Jest 是 Facebook 出品的一个测试框架,相对其他测试框架,其有几大特点:

    • 内置了常用的测试工具,比如自带断言、测试覆盖率工具,实现了开箱即用。
    • 作为一个面向前端的测试框架, Jest 可以利用其特有的快照功能,通过比对 UI 代码生成的快照文件,实现对 Vue、React 等框架的自动测试。
    • Jest 的测试用例是并行执行的,而且只执行发生改变的文件所对应的测试,测试速度快。

    jest 框架

    jest 安装以及简单配置

    1、安装

    npm install jest -D
    

    2、在 package.json 中指定 test 脚本

    "scripts": {
        "test": "jest"
      }
    

    3、jest 配置文件

    npx jest --init
    

    基本使用

    jest 自动化测试,提供单元测试(模块测试)、集成测试(多个模块测试),因此使用 jest,一定要引入模块的概念,测试的一定是模块

    用例的表示

    表示测试用例是一个测试框架提供的最基本的 API, 用 test()函数来描述一个测试用例,举个简单的例子:

    // math.js
    export function add(a, b) {
      return a + b;
    }
    //math.test.js
    import { add } from "./math";
    test("add方法", () => {
      expect(add(1, 2)).toBe(3);
    });
    

    其中 toBe(‘3)便是一句断言( Jest 管它叫 matcher ),写完了用例,运行在项目目录下执行npm test,即可看到测试结果, 也可以开启监听 -,不用每次手动执行

    异步模块的测试
    • 回调
    //async.js
    export const fetchDataCallback = (fn) => {
      axios.get("http://www.dell-lee.com/react/api/demo.json").then((res) => {
        fn(res.data);
      });
    };
    //async.test.js
    //不要这样写!此测试就在没有调用回调函数前结束
    test("fetchDataCallback", () => {
      fetchDataCallback((res) => {
        expect(res).toEqual({
          success: true,
        });
      });
    });
    
    //执行done()之后运行测试用例
    test("fetchDataCallback", (done) => {
      fetchDataCallback((res) => {
        try {
          expect(res).toEqual({
            success: true,
          });
          done();
        } catch (err) {
          done(err);
        }
      });
    });
    
    • promise
    //fetchData
    export const fetchData = () => {
      return axios.get("http://www.dell-lee.com/react/api/demo.json");
    };
    
    //fetchData.test.js
    //不要忘记return,否则promise解决之前,测试就已经被视为已经完成
    test("fetchData", () => {
      return fetchData().then((res) => {
        expect(res.data).toEqual({
          success: true,
        });
      });
    });
    

    使用 .resolves 匹配器,Jest 将等待此 Promise 解决

    test("fetchData", () => {
      return expect(fetchData()).resolves.toMatchObject({
        data: {
          success: true,
        },
      });
    });
    

    使用 async 和 await

    test("fetchData", async () => {
      const res = await fetchData();
      expect(res.data).toEqual({
        success: true,
      });
    });
    

    组合使用 async/await 和.resolves 匹配器

    test("fetchData", async () => {
      await expect(fetchData()).resolves.toMatchObject({
        data: {
          success: true,
        },
      });
    });
    
    钩子函数

    假如有如下的一个场景:

    export default class Counter {
      constructor() {
        this.number = 0;
      }
      addOne() {
        this.number += 1;
      }
      minusOne() {
        this.number -= 1;
      }
    }
    
    

    这样一个类,对他写测试用例

    import Counter from "./hook";
    let counter = new Counter();
    
    test("addOne,", () => {
      counter.addOne();
      expect(counter.number).toBe(1);
    });
    
    test("minusOne,", () => {
      counter.minusOne();
      expect(counter.number).toBe(-1);
    });
    
    

    假如这样运行,会发现每次都通不过,这是因为在第一个测试用例执行之后,number属性已经从 0 变成 1,执行第二个用例,number就 1 的基础上减 1,两个用例互相影响,jest 提供了钩子函数来解决这个问题

    let counter = null;
    beforeEach(() => {
      counter = new Counter();
    });
    test("addOne,", () => {
      counter.addOne();
      expect(counter.number).toBe(1);
    });
    
    test("minusOne,", () => {
      counter.minusOne();
      expect(counter.number).toBe(-1);
    });
    

    除此之外,jest 还提供其他钩子函数

    //所有用例执行之前
    beforeAll(() => {});
    //每个用例执行之前
    beforeEach(() => {});
    //每个用例执行之后
    afterEach(() => {});
    //所有用例执行之后
    afterAll(() => {});
    

    describe用例分组 钩子函数作用域,每一个describe都可以有自己的钩子函数,执行的顺序是,先执行外层的钩子函数,在执行内层的钩子函数

    
      beforeAll(() => {
    
      });
    
      describe("分组1", () => {
        beforeEach(() => {});
        test("addOne,", () => {
          counter.addOne();
          expect(counter.number).toBe(1);
        });
    
        test("addTwo,", () => {
          counter.addTwo();
          expect(counter.number).toBe(2);
        });
      });
    
      describe("分组2", () => {
        beforeEach(() => {});
        test("minusOne,", () => {
          console.log("minusOne");
          counter.minusOne();
          expect(counter.number).toBe(-1);
        });
    
        test("minusTwo,", () => {
          counter.minusTwo();
          expect(counter.number).toBe(-2);
        });
      });
    });
    
    jest 中的 mock
    • mock 函数 假如这样一个模块,传入一个 callback,然后执行 callback
    export const runCallback = (callback) => {
      callback();
    };
    

    想当然我们写一个回调函数,返回个hello字符串

    test("runCallback", () => {
      const func = () => "hello";
      expect(runCallback(func)).toBe("hello");
    });
    

    运行测试用例,结果报错,这是因为,在原模块中没有返回 callback 的执行结果,如果为了测试要去手动修改代码,显然不太合适,jest 提供函数 mock,用法如下:

    test("returnCallback", () => {
      //创建mock函数
      const func = jest.fn();
      //模拟函数返回值
      func.mockReturnValue("callbackDone");
      returnCallback(func);
      expect(func.mock.results[0].value).toBe("callbackDone");
    });
    

    作用: 捕获函数的调用次数和返回结果,自由设置返回结果

    • mock Ajax ajax 请求的结果应该是后端测试的内容,前端只需要关注请求是否发出,jest 提供对 ajax 的 mock,模拟 ajax 请求而不会真正发出请求,能提高了自动化测试的效率
    //ajax
    export const getData = () => {
      return axios.get("/api").then((res) => res.data);
    };
    
    //test
    import Axios from "axios";
    //mock axios,不会真正发送ajax请求
    jest.mock("axios");
    
    test("getData", async () => {
      //模拟ajax请求返回结果
      Axios.get.mockResolvedValue({ data: "hello" });
    
      await getData().then((data) => {
        expect(data).toBe("hello");
      });
    });
    
    • Timer Mocks  对使用延时器模块进行测试,由于延时时间不一定,每次测试都要等待,影响测试效率,jest 能够模拟延时器,加快测试用例执行
    export const timer = (callback) => {
      setTimeout(() => {
        callback();
      }, 3000);
    };
    
    jest.useFakeTimers();
    test("timer", () => {
      const fn = jest.fn();
      timer(fn);
      //快进3000ms
      jest.advanceTimersByTime(3000);
      expect(fn).toHaveBeenCalledTimes(1);
    });
    
    • ES6 Class Mocks 有一个 Es6 class 工具类,内部方法很复杂
    class Util {
      init() {
        return 1;
        //.....
      }
      a() {
        return 2;
        //...
      }
      b() {
        return 3;
        //....
      }
    }
    
    export default Util;
    

    有一个模块引用工具类中的方法

    import Util from "./util";
    
    export const demoFun = () => {
      const util = new Util();
      util.a();
      util.b();
    };
    

    我们要对这个模块进行测试, 通过 jest 可以对工具类进行 mock,简化外部模块,方便单元测试

    jest.mock("./util");
    //jest.mock发现Util是一个类,会自动把类的构造函数和方法变成jest.fn()
    //const Util = jest.fn();
    //Util.init = jest.fn();
    //Util.a = jest.fn();
    //Util.b = jest.fn();
    import Util from "./util";
    import { demoFun } from "./demoFun";
    test("测试demoFun", () => {
      demoFun();
      expect(Util).toHaveBeenCalled();
      expect(Util.mock.instances[0].a).toHaveBeenCalled();
      expect(Util.mock.instances[0].b).toHaveBeenCalled();
    });
    
    jest 中的 snapShoot 快照

    下面是对一个配置文件做测试

    //config.js
    export const generateConfig = () => {
      return {
        server: "http://localhost",
        port: 8080,
      };
    };
    //config.test.js
    import { generateConfig } from "./generateConfig";
    
    test("toMatchSnapshot", () => {
      expect(generateConfig()).toEqual({
        server: "http://localhost",
        port: 8080,
      });
    });
    

    上面的测试用例能够通过,但是当配置文件修改时,要同步测试用例,jest 提供 snapShoot 快照对配置文件测试

    • 生成单独的快照文件
    test("toMatchSnapshot", () => {
      expect(generateConfig()).toMatchSnapshot();
    });
    
    • 行内快照,无独立快照文件,需要先安装 pretter
    npm install pretter --save
    
    test("toMatchInlineSnapshot", () => {
      expect(generateConfig()).toMatchInlineSnapshot({
        date: expect.any(Date),
      });
    });
    
    生成测试覆盖率报告
    npx jest --coverage
    

    Vue 单元测试

    单元测试是对应用程序最小的单元运行测试的过程。通常是函数,但在 VUE 中,组件也是单元,因为组件的本质就是函数。

    组件的单元测试

    单元是应用中最小的可测试部分。在 VUE 中,组件与函数都是可测试的单元。

    搭载测试环境

    对于新项目来说,可以使用VUE-CLI工具创建项目

    基于jest的前端自动化测试 基于jest的前端自动化测试

    只需要在选择配置时选Unit Testing单元测试,并选择JEST作为测试框架即可

    而对于现有项目而言(针对@vue/cli)想增加jest测试模块。运行以下命令行就会帮我们去安装jest模块。

    vue add unit-jest
    

    起源地下载网 » 基于jest的前端自动化测试

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元