热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

redis内核单元测试框架

文章系转载,方便整理和归纳,源文地址https:developer.aliyun.comarticle62845简介:本文将对Redis内

文章系转载,方便整理和归纳,源文地址 https://developer.aliyun.com/article/62845


简介: 本文将对Redis内核单元测试框架进行基本的解析,并对如何编写测试用例进行基本的讲解。

在修改Redis内核之后,第一步我们需要做的就是添加或者对应的单元测试用例来进行基本的单元测试。本文将对Redis内核单元测试框架进行基本的解析,并对如何编写测试用例进行基本的讲解。


单元测试框架流程

Redis单元测试框架是基于tcl sh脚本实现的,其启动的方式为runtest [options]。
每一类的测试case写在单独的测试文件中,测试文件列表写入到test_server中all_tests列表中。
在启动测试时,会以server模式启动一个测试服务器,再启动多个测试客户端与之通信。由测试服务器会给空闲的测试服务端发送测试任务,参数为测试用例所在脚本文件名,由测试客户端执行对应的测试用例。详细的流程图如下:




1. processOptions

对选项进行解析,其中默认的模式是server模式,进入test_server_main函数; 若带了client选项则进入test_client_main函数.


2. test_server_main


  1. 内部维护了一系列当前的测试客户端状态列表;
  2. accept_test_clients 创建一个socket fd,侦听来自测试客户端的消息;
  3. 按照传入的参数,以runtest --client的方式启动n个测试client;

3. test_client_main

启动测试客户端,往测试服务器的fd上发送ready消息,开启客户端与服务端的交互流程;


4. 客户端与服务端的交互


  1. 客户端启动后,往测试服务器fd上发送ready消息;
  2. 服务端收到客户端ready或done事件后,检查所有的测试集,若还有测试任务未完成,则使用signal_idle_client方法往测试客户端发送测试任务,即"run 测试用例脚本文件名"消息;
  3. 客户端收到run消息后,调用execute_tests $data方法执行测试用例脚本文件;
  4. 客户端执行完测试case脚本后,往服务端发送done事件。再次进入第2步;

测试用例编写


1. 增加测试用例

新建一个测试用例文件,比如dummy.tcl,将之加入到test_helper.tcl的all_tests列表里

set ::all_tests {unit/auth...unit/dummy...
}

这样启动测试的时候,会自动执行unit/dummy.tcl里面的测试用例;


2. 测试用例文件

每个测试用例文件里面可以包含多个start_server的部分,每个start_server都会启动一个redis实例。
每个start_server内部包含多个test函数模块,每个test函数对应一个测试用例。
例子:auth.tcl

start_server {tags {"auth"}} {test {AUTH fails if there is no password configured server side} {catch {r auth foo} errset _ $err} {ERR*no password*}
}start_server {tags {"auth"} overrides {requirepass foobar}} {test {AUTH fails when a wrong password is given} {catch {r auth wrong!} errset _ $err} {ERR*invalid password}test {Arbitrary command gives an error when AUTH is required} {catch {r set foo bar} errset _ $err} {NOAUTH*}test {AUTH succeeds when the right password is given} {r auth foobar} {OK}test {Once AUTH succeeded we can actually send commands to the server} {r set foo 100r incr foo} {101}
}

3. 启动redis实例


启动单个实例

使用start_server可以启动一个redis实例. 启动的时候接受三种类型的参数:


  1. config: redis server的配置文件名,文件放到tests/assets目录下;
  2. override: 覆盖配置文件中的某个具体配置;
  3. tags: 该server的标示,一般用于log输出;
    启动一个redis实例的例子可以见上一节的auth.tcl.

启动多个实例

在进行主从同步测试,集群测试的时候,需要同时起多个redis实例,直接在一个test_server内部,再执行test_server即可。
例子:

start_server {tags {"repl"}} {start_server {} {test {First server should have role slave after SLAVEOF} {r -1 slaveof [srv 0 host] [srv 0 port]after 1000s -1 role} {slave}}
}

4. 执行redis命令

测试case中执行redis命令用r函数.(s函数与r函数类似,只是s函数会从info中提取返回值)

proc r {args} {set level 0if {[string is integer [lindex $args 0]]} {set level [lindex $args 0]set args [lrange $args 1 end]}[srv $level "client"] {*}$args
}

当同时启动多个redis实例时,使用r函数的第一个参数,标示具体在哪个实例上执行对应的命令。0为当前redis实例,-1为上一个启动的redis实例,以此类推。例如:

start_server {tags {"repl"}} {r set mykey foostart_server {} {test {Second server should have role master at first} {s role} {master}test {SLAVEOF should start with link status "down"} {r slaveof [srv -1 host] [srv -1 port]s master_link_status} {down}}
}

5. 结果判断

结果判断有几种方式:


  • assert类:详见support/test.tcl
  • fail “comment”: 失败
  • test函数最后一个参数,支持正则表达式。其匹配的对象是最后一条redis命令返回的结果。例如:

test {AUTH fails when a wrong password is given} {catch {r auth wrong!} errset _ $err} {ERR*invalid password}test {Arbitrary command gives an error when AUTH is required} {catch {r set foo bar} errset _ $err} {NOAUTH*}

6. 同步等待函数

wait_for_condition {maxtries delay e else elsescript}函数可以同步等待指定条件被满足。例:

test "Fixed AOF: Keyspace should contain values that were parseable" {set client [redis [dict get $srv host] [dict get $srv port]]wait_for_condition 50 100 {[catch {$client ping} e] == 0} else {fail "Loading DB is taking too much time."}assert_equal "hello" [$client get foo]assert_equal "" [$client get bar]}

7. 随机生成数据

start_write_load {host port seconds}函数可以不停的往实例中写入数据。


8. 一个稍复杂的例子

下面是一个主从同步的例子,作为这一节的结束和测试。

foreach dl {no yes} {start_server {tags {"repl"}} {set master [srv 0 client]$master config set repl-diskless-sync $dlset master_host [srv 0 host]set master_port [srv 0 port]set slaves {}set load_handle0 [start_write_load $master_host $master_port 3]set load_handle1 [start_write_load $master_host $master_port 5]set load_handle2 [start_write_load $master_host $master_port 20]set load_handle3 [start_write_load $master_host $master_port 8]set load_handle4 [start_write_load $master_host $master_port 4]start_server {} {lappend slaves [srv 0 client]start_server {} {lappend slaves [srv 0 client]start_server {} {lappend slaves [srv 0 client]test "Connect multiple slaves at the same time (issue #141), diskless=$dl" {# Send SALVEOF commands to slaves[lindex $slaves 0] slaveof $master_host $master_port[lindex $slaves 1] slaveof $master_host $master_port[lindex $slaves 2] slaveof $master_host $master_port# Wait for all the three slaves to reach the "online"# state from the POV of the master.set retry 500while {$retry} {set info [r -3 info]if {[string match {*slave0:*state=online*slave1:*state=online*slave2:*state=online*} $info]} {break} else {incr retry -1after 100}}if {$retry == 0} {error "assertion:Slaves not correctly synchronized"}# Wait that slaves acknowledge they are online so# we are sure that DBSIZE and DEBUG DIGEST will not# fail because of timing issues.wait_for_condition 500 100 {[lindex [[lindex $slaves 0] role] 3] eq {connected} &&[lindex [[lindex $slaves 1] role] 3] eq {connected} &&[lindex [[lindex $slaves 2] role] 3] eq {connected}} else {fail "Slaves still not connected after some time"}# Stop the write loadstop_write_load $load_handle0stop_write_load $load_handle1stop_write_load $load_handle2stop_write_load $load_handle3stop_write_load $load_handle4# Make sure that slaves and master have same# number of keyswait_for_condition 500 100 {[$master dbsize] == [[lindex $slaves 0] dbsize] &&[$master dbsize] == [[lindex $slaves 1] dbsize] &&[$master dbsize] == [[lindex $slaves 2] dbsize]} else {fail "Different number of keys between masted and slave after too long time."}# Check digestsset digest [$master debug digest]set digest0 [[lindex $slaves 0] debug digest]set digest1 [[lindex $slaves 1] debug digest]set digest2 [[lindex $slaves 2] debug digest]assert {$digest ne 0000000000000000000000000000000000000000}assert {$digest eq $digest0}assert {$digest eq $digest1}assert {$digest eq $digest2}}}}}}
}

总结

Redis内核自动化测试框架可以同时启动多个测试客户端进行测试,其测试用例编写简便,测试效率高,使用起来非常方便。
该测试框架也可以很方便的改造成其它基于socket通信的服务的自动化测试框架。
简约,高效,这就是我对它的印象。


推荐阅读
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 在ubuntu服务器上安装vscode,但是目前使用的方法都无法成功。第一次安装经历:安装完anaconda后有自动安装vscode的选项,输入yes后,没有出现错误,但是在终端输 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • 网卡工作原理及网络知识分享
    本文介绍了网卡的工作原理,包括CSMA/CD、ARP欺骗等网络知识。网卡是负责整台计算机的网络通信,没有它,计算机将成为信息孤岛。文章通过一个对话的形式,生动形象地讲述了网卡的工作原理,并介绍了集线器Hub时代的网络构成。对于想学习网络知识的读者来说,本文是一篇不错的参考资料。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
author-avatar
mobiledu2502894115
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有