文章目录
DSL 是什么?
DSL的概念:以极其高效的方式描述特定领域的对象、规则和运行方式的语言
DSL 其实是 Domain Specific Language 的缩写,中文翻译为领域特定语言(下简称 DSL);而与 DSL 相对的就是 GPL,这里的 GPL 并不是我们知道的开源许可证,而是 General Purpose Language 的简称,即通用编程语言,也就是我们非常熟悉的 Objective-C、Java、Python 以及 C 语言等等。
Wikipedia 对于 DSL 的定义还是比较简单的:
A specialized computer language designed for a specific task.
为了解决某一类任务而专门设计的计算机语言。
与 GPL 相对,DSL 与传统意义上的通用编程语言 C、Python 以及 Haskell 完全不同。通用的计算机编程语言是可以用来编写任意计算机程序的,并且能表达任何的可被计算的逻辑,同时也是 图灵完备 的。
但是在里所说的 DSL 并不是图灵完备的,它们的表达能力有限,只是在特定领域解决特定任务的。
A computer programming language of limited expressiveness focused on a particular domain.
另一个世界级软件开发大师 Martin Fowler 对于领域特定语言的定义在笔者看来就更加具体了,DSL 通过在表达能力上做的妥协换取在某一领域内的高效。
而有限的表达能力就成为了 GPL 和 DSL 之间的一条界限。
常见 DSL :
- Regex:正则表达式仅仅指定了字符串的 pattern,其引擎就会根据 pattern 判断当前字符串跟正则表达式是否匹配。
- SQL:SQL 语句在使用时也并没有真正的执行,我们输入的 SQL 语句最终还要交给数据库来进行处理,数据库会从 SQL 语句中读取有用的信息,然后从数据库中返回使用者期望的结果。
- HTML & CSS:HTML 和 CSS 只是对 Web 界面的结构语义和样式进行描述,虽然它们在构建网站时非常重要,但是它们并非是一种编程语言,正相反,我们可以认为 HTML 和 CSS 是在 Web 中的领域特定语言。
TC - DSL 是什么?
TC - DSL(TestCase Description),测试案例描述语言,用于描述测试案例的测试步骤。
有这个想法是因为我发现很多测试项有大量的重复垃圾代码,打印 log 混乱,并且没有办法轻松看出一个测试项是在测试什么,测试步骤是什么,什么时间点发生了什么,无法快速组合出新的测试项等等很多问题。
为了解决这个问题,我首先是想到了使用 json, html 等语言来实现可配置,可是这些语言从一定程度上我感觉很繁琐,描述效果也不是很好,和我想要描述的效果有一定的差距,所以自己想出了一种语法,可以较好的描述测试项,报告测试步骤。
一、TC 语法
语法高亮使用 vscode 制作插件实现,文件后缀为 .tc
。
Key Word
auto
:包含一系列 TestStep,后需要使用{}
或()
描述有哪些测试步骤import
:导入其他.tc
作为一个 TestStep
TestStep
- 由 Key Word 一系列 TestStep 组成的也称为一个 TestStep
- 图片中 green word 部分是单独 TestStep
Execution order
只有 auto
后可以使用 {}
或 ()
修饰。
{}
:代表顺序执行,执行完一个 TestStep 后执行下一个 TestStep()
:代表并行执行,TestStep 同时执行,所有 TestStep 运行完毕后结束,或是 TestStep fail 提前结束
Parameters
参数使用 []
表示,每一个参数用键值对表示,一般形式为 key=val
,val
有如下形式:
num
:可以是int
或float
,如5
、0.15
bool
:可以是true
True
false
False
,其中true
可省略,如图中random
没有=
默认为true
string
:字符串使用""
表示,转义字符只有\"
TestStep 有一些共有的参数:
timeout
:TestStep 需要在该时间内返回,否则认为超时,判断为 failloop
:循环测试次数,将该 TestStep 测试$(loop)
次duration
:该参数只作用于auto ()
和其他实现该参数的 TestStep,duration
与timeout
在auto ()
上的行为和其他 TestStep 有一些区别,如果auto ()
内层 TestStep 有定义该参数则使用内层 TestStep 的duration
,否则auto ()
会将duration
参数传递到内层 TestStep,
Comment
/* */
:多行注释//
:单行注释
二、TC 实现
1. lexical & syntax analysis
因为 TC 的语法很简单,词法分析和语法分析也比较简单,毕竟不是真正的编程语言,做起来没有什么难度。
2. TestStep
从前面可以看出,TestStep 大概分为三类:
- 单独的 TestStep
- 顺序执行的 TestStep
- 并行执行的 TestStep (多进程,据说 python 多线程不是真正的并发)
所以抽象为几个类:
class TestStep:
pass # 单独的 TestStep, 抽象类,不应该直接实例化,实现一个具体的 TestStep 时继承它
class __TestStepList(TestStep):
pass # 中间层抽象类,不直接实例化,管理 “一系列 TestStep”
class TestStepSeq(__TestStepList):
pass # 顺序执行 TestStep
class TestStepSync(__TestStepList):
pass # 并行执行 TestStep
三、TC 使用
项目文件结构:
AutoTest # 顶级目录
├── auto.py # 由 'auto.py' 执行 .tc
├── only_for_test
│ ├── client.py
│ └── server.py
├── README.md
├── testCase
│ └── test.tc
└── TestStep # 存放 TestStep
├── core # 核心内容,语法分析、TestStep 基类等
│ ├── .......
│ └── base_TestStep.py
├── .......
└── ts_hello.py # 普通的 TestStep
Usage:
python3 auto.py <xxx.tc> [show]
使用方法:
python3 auto.py xxx.tc
:开始执行xxx.tc
所描述的测试项python3 auto.py xxx.tc show
:查看xxx.tc
测试项的测试内容
需要添加新的 TestStep 时,继承 class TestStep
,然后使用基类提供的一些 API,最后将新类命名为文件名(即:ts_hello.py
中定义 class ts_hello(TestStep)
),并且放入 TestStep
文件夹即可,会自动搜索这些测试步骤。
例子:
以前面语法介绍的 .tc
为例:python3 auto.py testCase/test.tc show
xhr@ubuntu:~/study/python/AutoTest$ python3 auto.py testCase/test.tc show
==================================================
[1] import :<class 'ts_test_sync.ts_test_sync'>
[2] import :<class 'ts_send_data.ts_send_data'>
[3] import :<class 'ts_ping.ts_ping'>
[4] import :<class 'ts_hello.ts_hello'>
[5] import :<class 'ts_shell.ts_shell'>
[6] import :<class 'ts_fail.ts_fail'>
[7] import :<class 'ts_connect.ts_connect'>
[8] import :<class 'ts_goodbye.ts_goodbye'>
[9] import :<class 'ts_sleep.ts_sleep'>
[10] import :<class 'ts_test_seq.ts_test_seq'>
==================================================
--------------------------------------------------
TestStep [TestStepSeq] None: {'timeout': 60, 'loop': 2}
TestStep-1 [ts_shell] shell "echo 'start test'": {'cmd': "echo 'start test'"}
TestStep-2 [ts_connect] connect to a server, client=host_default_client_random__: {'timeout': 1.5}
TestStep-3 [TestStepSync] [Concurrent] : {'duration': 3}
TestStep-3-1 [ts_ping] ping a PC: {'timeout': 7, 'loop': 2, 'duration': 5}
TestStep-3-2 [ts_hello] Print "hello" repeatedly: {'timeout': 50, 'duration': 3}
TestStep-3-3 [ts_hello] Print "hello" repeatedly: {'timeout': 50, 'duration': 3}
TestStep-3-4 [ts_ping] ping a PC: {'timeout': 50, 'duration': 3}
TestStep-4 [ts_send_data] send data to server, client=host_default_client_random__: {'timeout': 1.5}
TestStep-5 [TestStepSync] [Concurrent] : {'duration': 5}
TestStep-5-1 [ts_hello] Print "hello" repeatedly: {'random': True, 'duration': 5}
TestStep-5-2 [ts_ping] ping a PC: {'random': False, 'duration': 5}
TestStep-6 [ts_goodbye] Print once "goodbye": {'loop': 3}
--------------------------------------------------
这个简单的 Demo 还是运行得还不错,蛮有意思的,还有很多方面仍然有优化的余地。
源码
Github 实在是太慢了。。