测试基础知识

测试框架jest

  • 开箱即用:基本不需要额外的配置即可使用
  • 功能强大:自带断言、测试覆盖率工具,支持Mock、Snapshot、异步测试等
  • 应用广泛:已经成为了vue cli和create-react-app默认集成的测试框架
  • 文档丰富:jest DOC

test suit / test case

Test Case:测试用例,表示对一个功能点的测试

Test Suit:测试套件,表示一组相关用例的组合

const myBeverage = {
    delicious: true,
    sour: false,
}
// test unit
describe( 'my beverage',() => {
    // test case
    test('is delicious', () => {
        expect(myBeverage.delicious).toBeTruthy();
    })
    // test case
    test('is not sour', () => {
        expect(myBeverage.sour).toBeFalsy();
    })
})

断言:

在程序设计中,断言是一种一阶逻辑,目的是表示与验证开发者预期的结果。

当程序运行到断言的位置时,对应的断言应该为真。若断言为假时,程序会中止并给出错误消息

JS中的断言:

  • 浏览器: console.assert
  • Node: assert模块
  • 第三方自定义: 例如Vuex
function assert (condition, msg) {
    if (!condition) {
        throw new Error(`[Vuex] ${msg}`)
    }
}

Jest中的断言
Jest中内置了很多断言方法,称作“Matcher”

Matcher的使用

test('object assignment!',() => {
    const data = {one: 1};
    data[ 'two'] = 2;
    expect(data).toEqual({one: 1, two: 2});
})

Jest中的Matchers,包含各种数据类型的各种判断方法,这里只列举常见的,详细文档见 jest macher

test('null',() =>{
    const n = null;
    expect(n).toBeNull();
    expect(n).toBeDefined();
    expect(n).not.toBeUndefined();
    expect(n).not.toBeTruthy();
    expect(n).toBeFalsy();
})

test( 'two plus two', () => {
    const value = 2 + 2:
    expect(value).toBeGreaterThan(3);
    expect(value ).toBeGreaterThanOrEqual(3.5);
    expect(value ).toBeLessThan(5);
    expect(value ).toBeLessThan0rEqual(4.5);
    expect(value).toEqual(4);
})

异步测试:

在JavaScript中执行异步代码是很常见的。 当你有以异步方式运行的代码时,Jest 需要知道当前它测试的代码是否已完成,然后它可以转移到另一个测试。 Jest有若干方法处理这种情况。

异步测试 - Promise

用例中返回一个Promise,则Jest会等待Promise的resove状态 如果 Promise 的状态变为 rejected, 测试将会失败

let fetchData = Promise.resolve("peanut butter")
test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});

异步测试 - Async/Await

let fetchData = Promise.resolve("peanut butter")
test('the data is peanut butter', async () => {
  const data = await fetchData();
  expect(data).toBe('peanut butter');
});

 异步测试 - callback

test('the data is peanut butter', done => {
  function callback(error, data) {
    if (error) {
      done(error);
      return;
    }
    try {
      expect(data).toBe('peanut butter');
      done();
    } catch (error) {
      done(error);
    }
  }

  fetchData(callback);
});

辅助函数 

写测试的时候你经常需要在运行测试前做一些准备工作,和在运行测试后进行一些收尾工作。 Jest 提供辅助函数来处理这个问题。

  • beforeEach
  • afterEach
  • beforeAll
  • afterAll
/在该文件中的所有test运行之前,都会执行
beforeEach(() => {
    return initializeCityData()
})
test('city database has Vienna',() => {
    expect( isCity('Vienna' )).toBeTruthy();
})
describe('matching cities to foods',() => {
    // 只会在这个test suit的每个test前运行
    beforeEach(() => {
        return initializeFoodDatabase( );
    })
    test('Vienna <3 sausage',() => {
        expect(isValidCityFoodPair('Vienna','Wiener Schnitzel')toBe(true );
    })
});

Mock

Mock是一种覆盖原有函数、类的实际实现,来检测其调用情况的一种测试方法

const forEach = require('./forEach');

const mockCallback = jest.fn(x => 42 + x);

test('forEach mock function', () => {
    forEach([0, 1], mockCallback);

    // mockCallback被调用2次
    expect(mockCallback.mock.calls.length).toBe(2);
    // mockCallback的第一次调用的第一个参数应该是0
    expect(mockCallback.mock.calls[0][0]).toBe(0);
    //mockCallback的第二次调用的第一个参数应该是1
    expect(mockCallback.mock.calls[1][0]).toBe(1);
    //mockCallback的第一次调用的结果应该是42
    expect(mockCallback.mock.results[0].value).toBe(42);
});

Mock一个模块 

//users.js

import axios from 'axios';
class Users {
  static all() {
    return axios.get('/users.json').then(resp => resp.data);
  }
}
export default Users;
//users.test.js

import axios from 'axios';
import Users from './users';

//用 jest.mock(...) 函数自动模拟 axios 模块
jest.mock('axios');

test('should fetch users', () => {
  const users = [{name: 'Bob'}];
  const resp = {data: users};

  //模拟模块时为 .get 提供了一个 mockResolvedValue ,它会返回假数据用于测试
  axios.get.mockResolvedValue(resp);

  return Users.all().then(data => expect(data).toEqual(users));
});

Snapshot 

Snapshot也称快照,是一种非常强大的测试工具,一般用于UI组件的测试

实现是通过将某次的执行结果拍照做为后续ui比对的参考,所以组件UI变更后应该更新组件的快照

import renderer from 'react-test-renderer';
import Link from '../Link';

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.facebook.com">Facebook</Link>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});
//生成快照文件内容
exports[`renders correctly 1`] = `
<a
  className="normal"
  href="http://www.facebook.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
>
  Facebook
</a>
`;

 测试覆盖率

测试覆盖率是用于衡量一个工程测试代码完整程度的指标,Jest内置了Istanbul模块,可以从以下 4个维度统计测试覆盖率:

  • Statements 语句覆盖率,即所有语句的执行率
  • Branches 分支覆盖率,即所有代码分支如if、三目运算的执行率
  • Functions 函数覆盖率,即所有函数的被调用率
  • Lines 行覆盖率 即所有有效代码行的执行率,和语句类似,但是计算方式略有差别

lstanbul的原理
lstanbul在代码被执行之前,拦截了模块加载器,为其中的每一个逻辑分支、函数等添加了计数器,从而得到覆盖率结果。

测试报告

一次自动化测试的详情和最终结果,应该由一张 测试报告描述

 

测试分类

单元测试

单元测试应该只测试一个独立单元的工作

前端单元测试,往往是以组件、util为粒度

它的作用是提升软件质量,保证重构代码的安全性

详见 单元测试

E2E测试

End to End,即“端到端”

模仿用户,从某个入口开始,逐步执行操作,直到完成某项工作

关注一个完整的操作逻辑链是否能够完成

详见 E2E测试

最佳实践

过多和过少的测试都不好:平衡开发效率和代码质量是一个值得长期讨论的问题

用例不应和内部实现耦合:防止影响重构、造成测试用例“过拟合”

尽可能靠近边界条件:边界条件是最容易出现bug的地方

慎重使用 Mock 功能:Mock会造成测试偏离真实环境,这是我们应该尽量去避免的

编写Testable的代码:单一职责、无状态

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值