导读
之前的文章(《MySQL自动测试框架Test Framework浅析》)从源码级别对MySQL自动测试框架Test Framework进行了简要分析,本文接下来从实践的角度介绍Test Framework工具的使用方法。
1 简介
Test Framework主要应用于MySQL等相关数据库项目开发测试,对单个功能点或者模块进行正确性和功能性测试,该工具在SQL级别对MySQL进行单元测试。Test Framework不仅仅测试MySQL服务器和客户端的正确性,也能根据实际需要测试不同启动参数配置的MySQL服务器和客户端,同时支持mysqltest、innochecksum、mysql_upgrade、mysqladmin、mysqlpump等工具的验证方法。方便开发者模拟某一场景,验证数据库功能是否达到预期。
它的位置在MySQL源码同级的目录./mysql-test下,使用perl脚本编写。在编译MySQL源码完毕即可使用。
在mysql-test/目录下还有其他目录,列出其中几个常用的目录:
include目录:一些头文件,这些文件在*.test文件中调用,使用source命令引入;
lib目录:是一些库函数,主要被mtr脚本调用;
std_data目录:一些标准数据,某些测试用例可以直接拷贝使用即可。
suite目录保存套装组件的测试用例,方便测试同一类测试用例。
extra目录:一些binlog、rpl测试相关的文件。
t目录:默认main套件测试用例
r目录:默认main套件测试用例结果文件
2 Test Framework使用方法
Test Framework通过执行一个测试用例文件,将该测试用例的实际测试结果,与预期结果文件作对比。如果一致,则认为测试通过,无问题;否则,不一致,则测试失败,可以根据输出结果查找问题。
接下来首先介绍如何编写一个测试用例以及相应的文件,其次介绍如何使用Test Framework执行测试用例。
2.1编写测试用例和预期测试结果文件方法
测试用例的输入存储在一个文件中,运行测试的预期结果存储在另一个文件中。每个单独的测试用例后重启服务器并要求之前的数据是清空的。
测试用例文件主要由SQL语句组成,也可以使用测试语言结构来控制如何运行测试和验证它们的结果。测试用例文件统一放在t/文件夹下,文件名以.test为后缀。测试用例文件支持脚本语言函数。
预期结果文件与测试用例文件的大部分代码几乎一致,只是在文件中增加了输出结果。在有输出结果的语句后面(如查询语句select),还将输出结果写在预期结果文件中。预期结果文件统一放在r/文件夹下,文件名以.result为后缀。
测试用例文件应与预期结果文件同名,仅以后缀区分是测试用例文件还是预期结果文件,使用时加不加.test后缀执行都可以。
按照如上要求,下面是一个简单的测试用例文件,和一个与测试用例相对应的预期结果文件,为了对比内容的差异,特意用空行和虚线进行了标注:
测试用例.test文件内容: | 预期结果.result文件内容: |
--echo ---开始测试用例--- | ---开始测试用例--- |
--source include/xxxxx.inc | |
--disable_info | |
use test; | use test; |
create table stu(id int(4), name varchar(10))charset=utf8 engine=innodb; | create table stu(id int(4), name varchar(10))charset=utf8 engine=innodb; |
--enable_info | |
insert into stu values(1, “zhangsan”); | insert into stu values(1, “zhangsan”); |
affected rows: 1 | |
select * from stu; | select * from stu; |
id name 1 zhangsan affected rows: 1 | |
drop table stu; | drop table stu; |
affected rows: 0 | |
--echo 错误情况的示例 | 错误情况的示例 |
#--error 1049 | |
--error ER_BAD_DB_ERROR | |
use a; | use a; |
ERROR 42000: Unknown database 'a' affected rows: 1 | |
#use aaaaa; | |
--echo ---结束测试用例--- | ---结束测试用例--- |
可以看到,除了熟悉的SQL语句,还有一些以--开头的参数,这些参数起着很大的作用,可以根据实际需要在编写测试用例文件时进行增加。
关于更多的参数的介绍,可以到官网Testing Tools模块查看,这里列出比较常用的一些参数的含义,以供参考:
参数名称 | 描述 |
--source include/xxxx,inc | 等效于将目标文件的内容全部拷贝到当前位置,mysql-test/include目录下有很多这样的文件,他们提供了类似函数的功能,以简化每个测试用例的代码。注意souce前面的 --,大多数的非SQL语句都要求加--。 |
--enable_warnings | 打开警告信息的输出 |
--disable_warnings | 关闭警告信息的输出 |
--enable_info | 打开输出信息,在结果中多输出影响行数 |
--disable_info | 关闭输出信息 |
--enable_query_log | 输出原语句 |
--disable_query_log | 默认情况下,r/testname.result中会包含原语句和执行结果,若不想输出原语句,需要在t/testname.test文件头中加此参数。 |
--COMMAND | 可以使用部分Linux系统命令,COMMAND可以替换为如mkdir、rmdir、echo、exec等命令。执行效果同要替换的命令一样。 |
--exec xxx | 继续执行参数后面的某些命令。 |
--echo | 向标准输出回显输出信息。 |
--error N | 如果要测试出错语句,必须在testname.test文件中,会出错的语句之前加此参数。N为错误号或者宏。 |
--replace_column | 指定要用字符串替换给定列中的任何内容。特别适合处理每次测试运行得到不同的输出的情况。如因为时间因素而影响输出结果的。 |
--initialize | 初始化 |
--disable_abort_on_error | 禁用中止,可以在每次测试运行中看到更多的错误。 |
--enable_reconnect | 重连服务器 |
--shutdown_server N | 关闭服务器 |
--copy_file | 拷贝文件 |
--remove_file | 移除文件 |
--write_file | 往文件中写数据。 |
--cat_file | 查看文件内容。 |
--sorted_result | 对result文件进行排序。也可以使用SQL语句中的order by。 |
--replace_result | 替换result文件。 |
--repeat | 连续运行输入测试n次。非常适合诊断随机故障。 |
--nowarnings | 忽略整个验证server日志的过程,可以在文件头增加此参数。 |
注意:
如果需要在server启动前执行一些脚本或者配置文件,可以写在 t/目录下,*.opt文件是选项文件,指在这个测试中mysql以.opt文件的内容作为测试参数启动;*.sh文件是在执行启动mysql-server之前提前执行的脚本,由mtr自动执行。
每个测试用例会启动一个mysql服务,默认端口为13000。如果这个测试用例涉及到需要启动多个服务(比如主从),则端口从13000递增。关于主从:主库的配置写在testname-master.opt, 从库的写在testname-slave.opt。
如果服务端输出中有warning或error信息,则会导致退出,除非已经抑制特定的warnings和errors信息。
如果要指定忽略某些行,可以使用语句call mtr.add_suppression("xxxxxxx");,这样能够忽略""中的内容,""内允许使用正则表达式。
可以使用let进行赋值,如给变量赋值,let $ret= xxx,给sql语句的返回值赋值,如let $tmp=’select name from stu where id = 1’,也可以使用let给系统变量赋值let $path=$PATH。
测试文件中可以使用if、else、while等条件循环语句。如:
--disable_query_log
if ($debug)
{
--enable_query_log
}
在mysql-test-run.pl脚本中保存有运行的环境变量,如MTR_PORT_BASE、MYSQL_TEST_DIR等,这些环境变量可以在测试用例文件中直接使用,如果在测试用例中定义的环境变量,只可以在本测试用例或测试用例包含的文件中使用。
如果想要在测试用例中临时执行某些perl脚本语句,可以使用以perl;开头,中间为要执行的perl脚本语句,最后以EOF结尾,如:
perl;
print “This is a test\n” #perl脚本语句
……
EOF
通过用相应的值替换对文本中变量的引用来评估语句。然后将结果语句发送到服务器上执行。使用“$name”指定“$name”变量中的内容。使用eval statement相对于statement语句的优势在于eval提供了变量扩展。如:
let $DB=test
eval USE $DB;
使用popen()库调用执行shell命令,命令中对变量的引用将替换为相应的值,使用格式为:
exec command [arg] ...
如:
--exec $MYSQL_DUMP --xml --skip-create test
--exec rm $MYSQLTEST_VARDIR/tmp/t1
exec $MYSQL_SHOW test -v -v;
关于更多的编写测试用例方法,可以查看MySQL官网关于mysqltest命令参数的介绍。
2.2 Test Framework执行方法
语法:
shell> mysql-test-run.pl [options] [test_name] ...
mysql-test-run、mysql-test-run.pl、mtr三个文件一模一样,选择任一文件来执行即可,如./mtr。mtr实际上是调用的mysqltest来进行测试的。
常用选项参数解释:
参数名称 | 描述 |
[什么参数都不加] | 执行t/目录和suits/目录下所有以.test为后缀的测试用例文件(测试时间较长),并且,任何一个测试用例执行失败都导致整个执行计划推出。 |
--mysqld =--key_buffer_size=16384 | 将启动参数传给mysql服务器。每个选项必须有一个--mysqld打头,不能连在一起写。 所有的参数传递进去后执行一次。 |
--combination =xxxx,xxxxx,xx | 类似于--mysqld,但行为不同。执行多个测试运行。每一个combination参数传递进去参数执行一次,然后继续下一个参数执行。 如果有一个combination参数相当于--mysqld。 |
--do-test=events | 执行所有以events为前缀的测试用例文件(搜索范围为t/和所有的suite) --do-test的参数支持正则表达式,左边的命令等效于./mtr –do-test=events.* 所以如果想测试所有的包括innodb的case,可以用 ./mtr –do-test=.*innodb.* |
--skip-test=events | 将以events开头的所有测试用例跳过,支持正则表达式。 |
--vardir | mtr允许并行执行,需要特别指定不同的日志目录。 |
--suite=suite_name | suits目录下有多个目录,是一些测试的套餐。此命令单独执行suits/suite_name目录下的所有测试用例文件(其他的目录不执行)。 t/目录下的所有文件组成了默认的套餐main。 因此 ./mtr --suite=main则只执行t/*.test. |
--force | 忽略错误并继续执行直到所有的测试用例执行结束。 |
--parallel=auto | 以多线程执行测试用例 |
--record | 生成.result文件。将.test的实际测试结果.reject作为预期结果.result |
--result-file=file_name | 此选项指定测试用例预期结果的文件。-- result-file与-- record一起决定了mysqltest如何处理测试用例的测试实际结果和预期结果。 |
注意:
测试过程生成的所有数据都保存在var/目录下,其中var/install.db/目录在首次初始化后生成,后续直接拷贝给后续服务器的启动使用。var/log/目录下保存本次执行的日志文件,包括错误信息、警告信息、初始化过程的参数、执行的测试用例名以及执行时间文件。var/tmp/目录保存理你是的执行SQL语句的通过情况,包括两次check-testcase和check-warnings过程。var/mysqld.1/目录保存本次执行生成的数据文件。var/my.cnf是执行测试用例的配置文件。
测试用例要求尽量不要受到别的测试用例的影响,如一个测试用例创建了一个表,然后进行了一些操作后,最后应该删除这个创建的表,防止影响到别的测试用例的执行。
测试用例不通过,会显示出哪一行语句执行出错,并且将出错信息打印出来,接下来就可以根据出错信息进行有针对性的bug修复。
测试失败的可能原因有很多。比如中间执行了某个非法的语句;测试用例中每次执行的测试结果都不同等。可通过查看var/log/下的错误日志文件查找分析。
下面是成功和失败的输出结果图:
成功执行的结果图
执行失败的结果图
3、总结
使用Test Framework工具能够实现对MySQL等数据库系统的各项功能的进行SQL级别的自动测试,Test Framework框架能够随时根据需要进行增减修改相关模块,如果你熟悉其他数据库的机制原理,你完全可以将Test Framework打造成专属数据库的自动测试框架。