utdsl v0.0.1
- 为什么要使用TDSL
单元测试是开发中重要的一部分工作之一,但是常常,业务时间紧张,除了要加用例代码,还要维护旧的用例,每个人用例写法多少也有不一致,代码量大修改起来也难定位,前前后后也耗不少时间,不得不说,用例虽不难,但也会让我们投入不少精力。
由于用例大部分都是验证单元模块IO验证验证,核心代码比较固定和机械化,那么完全可以根据源码进行ast分析,解析出各个模块的io类型,并重新构造,直接生成用例代码,我们只需要指令式声明IO样本数据即可。为此,这里定义了一套简洁的描述语法规则TDSL(Test DSL)。
- TDSL引入的优势
所有语法必须写在函数前的块注释(/**/)中。然后自动生成对应的完成测试用例代码。
优势: 1,不用手写用例代码,不用维护 2,从注释直观开发UT测试io,面向指令,所有用例一目了然 3,统一用例代码,使用核心语法,保持一致性 (其实完全不用关心) 4,约束函数实现,约束一个函数只做一件io事情,手动编写的用例过于灵活,后面维护成本比较高 5,大大降低TDD成本 6,注释文档、逻辑、用例同步更新,避免同步的成本 7,方便统一IO样本数据存放 8,提高效率的同时对源码的组织结构形成固定约束 9,覆盖率低?不存在的
劣势: 增加注释量
- TDSL常用语法
为不对源码进行入侵,也不额外维护文件,TDSL使用注释语法规范。必须在块代码注释中声明,否则不生效,但也不产生任何影响。
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) => (returnValue)
/**
* 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文件内容为(支持两种形式)
// 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);
});
稍微完整的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);
});
5 years ago