tdml

0.0.4 • Public • Published

  TD(Test Description)ML, 测试描述标记语言,是一套用于高效简洁描述测试代码的规范语言。

  • 为什么要使用TDML

  单元测试是开发中重要的一部分工作之一,但是常常,业务时间紧张,除了要加用例代码,还要维护旧的用例,每个人用例写法多少也有不一致,同伴的用例代码也往往会含有特定逻辑,而且代码量大修改起来也难定位,前前后后也耗不少时间。不得不说,用例虽不难,但也会让我们投入不少精力。

  由于用例大部分都是验证单元模块IO验证验证,核心代码比较固定和机械化,那么可以根据源码进行ast分析,解析出各个模块的io类型,并重新构造,直接生成用例代码,我们只需要指令式管理IO样本数据即可。为此,这里定义了一套简洁的描述语法规则TDML(测试描述标记语法,Test Description ML)。TDML的基础理念是:同样的测试用例的代码只写需要工具一次,其他的交给TDML

  而至于底层使用mocha还是jest生成用例,可通过tdml.config.js配置。开发者只需要在注释中写指令语法就可以了。

  • TDML引入的优势

  所有语法必须写在函数前的块注释(/**/)中。然后自动生成对应的完整测试用例代码。

  优势:

1,不用手写用例代码,不用维护

2,从注释直观开发ut测试IO,面向指令,所有用例和模块IO一目了然。边界IO也更加直观

3,统一用例代码,使用核心语法,保持一致性 (其实完全不用关心)

4,约束源码实现,约束一个函数只做一件IO事情,手动编写的用例过于灵活,后面其它人维护成本较高

5,大大降低TDD成本

6,注释文档、逻辑、用例同步更新,避免同步的成本

7,统一IO数据样本文件管理,样本数据更易维护

8,提高效率的同时对源码的组织结构形成反向约束 (需要按照规范的方式写,才能自动生成用例)

9,覆盖率低?不存在的

  劣势: 增加注释量。

  • 安装使用
npm i tdml -g
tdml c [path] # 例如 tdml c ./src/code.js

  也可以安装在项目中书写tdml.config.js进行配置,并安装插件自动触发。

  • TDML常用语法

  为了不对源码进行入侵,也不额外维护文件,TDML使用注释语法规范。必须在块代码注释中声明,否则不生效,但也不产生任何影响。

1、判断相等

fn(...Params) => (returnValue),例如

/* name function B
 * this is test
 * B(1, 3) => (3)
 */
export function B (a: number, b: number): number {
    return a * b;
}

自动编译后

test("B module", () => {
    expect(B(1, 3)).toBe(3);
});

2、判断对象深度相等

fn(...Params) ==> (returnValue),例如

/* name B
 * this is test
 * B(1, 3) ==> (3)
 */
export function B (a: number, b: number): number {
    return a * b;
}

自动编译后

test("B module", () => {
    expect(B(1, 3)).toEqual(3);
});

3、判断对象不相等

fn(...Params) !=> (returnValue),例如

/* name B
 * this is test
 * B(1, 3) !=> (4)
 */
export function B (a: number, b: number): number {
    return a * b;
}

自动编译后

test("B module", () => {
    expect(B(1, 3)).not.toBe(4);
});

4、判断对象深度不相等

fn(...Params) !==> (returnValue),例如

/* name B
 * this is test
 * B(1, 3) !==> (4)
 */
export function B (a: number, b: number): number {
    return a * b;
}

自动编译后

test("B module", () => {
    expect(B(1, 3)).not.toEqual(4);
});

5,路径常量数据文件

使用冒号分割,冒号前为读取mock数据的属性key,后面为相对路径,带不带引号都可以,重复路径最终会自动合并

(:path, data.dataKey: path, ...params) => (data.dataKey: path)

/**
 * name B
 * A(a: './data.js', 3) => (3)
 * A(b:./data.js, d:"./data1.js") => (1)
 * A(a:./data.js, 1) => (a.b:./data1.js)
 */
export function B (a: number, b: number): number {
    return a * b;
}

其中data.js和data1.js文件内容为(支持export和module.exports导出)

// data.js
module.exports = {
    a: 1,
    b: 1
};

// data1.js
export default {
    c: 1,
    d: 1
}

自动编译后

const data1 = {
    a: 1,
    b: 1
};
const data2 = {
    c: 1,
    d: 1
}
test("B module", () => {
    expect(B(data1.a, 3)).toBe(3);
    export(B(data1.b, data2.d)).toBe(1);
    export(B(data1.a, 1)).toBe(data2.d);
});

6, 属性判断

fn(...Params).property => (lengthValue),例如

/* name B
 * this is test
 * B(1, 3).length => (1)
 * B(1, 3)['length'] => (1)
 * B(1, 3)['length']['xxx'] !=> (1)
 */
export function B (a: number, b: number): number {
    return a * b;
}

自动编译后

test("B module", () => {
    expect(B(1, 3).length).toBe(1);
    expect(B(1, 3)['length']).toBe(1);
    expect(B(1, 3)['length']['xxx']).not.toBe(1);
});

7,异步链式判断

fn(...params) -> (module1) ==> (returnValue)

  异步执行fn,然后判断module1的值是否和returnValue相符。

/**
 * 拉取按天时间段报表,内部操作为拉取一个接口,然后把接口数据dispatch到store上
 *
 * getOverviewChart({}, {}) -> (getState().apiData) ==> ({chart: {a:1}})
 */
export const getOverviewChart = (params = {}, options = {}) => (dispatch, getState) => {}

  编译后,这里需要配置tdml.config.js来决定是否使用dispatch

dispatch(getOverviewChart({}, {})).then(() => {
  expect(getState().apiData).toEqual({
    chart: {
      a: 1
    }
  });
  done();
}, () => {});

8,链式触发调用和断言

fn(...params) -> number1(module1:path) -> ... -> number2(module2:path) => (returnValue)

  fn调用时调用module1次数为number1,调用module2次数为number1,然后返回断言值为return

稍微完整的examples
/**
 * add(6, 3) !=> (4)
 * add(2, 3) ==> (6)
 * add(2, "3") !=> ("13")
 * add("abc", "df") => ("abcdf")
 * add(path.b: './data/data', "df") => ("abcdf")
 * add(:./data/data)['length']['xx'] => (234)
 * add(b.c:'./data/data1').length => (123)
 * add(b.c:./data/data1).length => (b.c: ./data/data3.js)
 */
export function add (a: number, b: number): number {
    return a + b;
}

编译后

const data3 = {
  "b": {
    "c": 4
  }
};
const data2 = {
  "data": [1, "kesd", 3, "oooo", {
    "a": 1
  }, {
    "a": 1,
    "b": 2,
    "c": {
      "a": 1,
      "b": 2,
      "c": [{
        "a": 1,
        "b": 2
      }]
    }
  }],
  "path": "mmmmmmmmmmmmm"
};
const data1 = {
  "data": [1, "kesd", 3, "oooo", {
    "a": 1
  }, {
    "a": 1,
    "b": 2,
    "c": {
      "a": 1,
      "b": 2,
      "c": [{
        "a": 1,
        "b": 2
      }]
    }
  }],
  "path": "mmmmmmmmmmmmm"
};
test("add module", () => {
  expect(add(6, 3)).not.toBe(4);
  expect(add(2, 3)).toEqual(6);
  expect(add(2, "3")).not.toBe("13");
  expect(add("abc", "df")).toBe("abcdf");
  expect(add(data1.path.b, "df")).toBe("abcdf");
  expect(add(data1)['length']['xx']).toBe(234);
  expect(add(data2.b.c).length).toBe(123);
  expect(add(data2.b.c).length).toBe(data3.b.c);
});

TODO:

1, 文件依赖自动解析

2, react集成配置解析 tdml.config.js

3,redux 集成配置解析 tdml.config.js

4,自动插件化 tdml.config.js

5,数据转换类模块用例分析(测试验证函数IO是否符合预期) 已完成

6,请求数据并dispatch数据类型模块分析(Mock接口数据,验证dispatch后对应节点数据是否符合预期)

7,ui事件触发类型函数分析(事件模拟,判断是否调用对应的其它模块以及调用的次数)

8,ui渲染类节点特性判断(渲染,然后节点查询对应的节点)

FUTURE TODO:

//

Readme

Keywords

none

Package Sidebar

Install

npm i tdml

Weekly Downloads

1

Version

0.0.4

License

ISC

Unpacked Size

41 kB

Total Files

19

Last publish

Collaborators

  • ouvenzhang