基于Groovy+HttpRestful的超轻量级的接口测试用例配置的设计方案及DEMO实现

时间:2023-03-09 23:06:41
基于Groovy+HttpRestful的超轻量级的接口测试用例配置的设计方案及DEMO实现

目标###

设计一个轻量级测试用例框架,接口测试编写者只需要编写测试用例相关的内容(入参及结果校验),不需要理会系统的实现,不需要写跟测试校验无关的内容。

思路###

测试用例分析####

一个用例由以下部分组成:

(1) 测试用例名称 ; (2) 接口名及URL/Path; (3) 接口入参; (4) 接口返回结果校验。

测试框架需要读取用例配置信息,根据指定接口及入参调用服务,并根据指定校验函数来对接口返回结果做检验,判断测试用例是否执行成功。

设计考量####

为了灵活调用不同接口,针对以上的配置,(2) 采用 http restful 的方式; (3) 采用 Map ; (4) 需要一套校验语法。这里暂时直接采用 groovy 脚本。

DEMO实现###

使用者需要做什么####

接口测试用例编写者只需要定义一个 TestCase 类即可。这个类含有如下信息:

(1) 待测试接口的 url : http://ip:7001 及 restful 路径 /xxx

(2) 带有 @Case 注解的方法。 里面返回一个 Map,

name: 测试用例名 ;

param: 入参map ;

check: 校验函数。 data 就是返回的顶层。

比如搜索测试用例类SearchTestCases:

package cc.lovesq.study.testcase.qa

import cc.lovesq.study.testcase.Case

class SearchTestCases {

    def url = 'http://ip:7001'
def path = '/searchApp/order/search' @Case
def get() {
return [
'name': 'testSearchOrderNo',
'param': ['shopId': 55, 'orderNo': 'E20180507200552032000001', 'source':'service-test'],
'check': { data ->
data.list.each {
order ->
order.orderNo == 'E20180507200552032000001'
}
}
]
} }

详情测试用例类

package cc.lovesq.study.testcase.qa

import cc.lovesq.study.testcase.Case

class DetailTestCases {

    def url = 'http://ip:7001'
def path = '/detailApp/orderInfo/byOrderNo' @Case
def get() {
return [
'name': 'testSingleOrderDetail',
'param': ['shopId': 55, 'orderNo': 'E20180507200552032000001', 'source':'service-test', 'bizGroup': 'trade'],
'check': { data ->
data.mainOrderInfo.orderNo == 'E20180507200552032000001'
}
]
} }

框架基本实现####

通过指定的 TestCase 类,读取其用例配置信息,识别其中的用例配置,通过Http调用接口,然后回调指定的校验函数来校验结果。

package cc.lovesq.study.testcase

import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import org.slf4j.Logger
import org.slf4j.LoggerFactory import static groovyx.net.http.Method.POST class CaseExecutor { static Logger log = LoggerFactory.getLogger(CaseExecutor.class) def invokeAllCases(testCase) {
Object tc = testCase
tc.getClass().getDeclaredMethods().findAll { // 识别测试用例: 带有 @Case
it.getAnnotation(Case.class) != null }.each {
it ->
def caseInfo = it.invoke(tc, null)
def result = exec(caseInfo['name'], tc.url, tc.path, caseInfo['param'], caseInfo['check'])
println("case=${caseInfo['name']}, result=${result}")
}
} def exec(name, url, path, param, check) { def http = new HTTPBuilder(url) def result http.request(POST) {
uri.path = path
requestContentType = ContentType.JSON
body = param response.success = { resp, json ->
def data = json.data.data
try {
log.info("Enter Test Case : {}", name)
check(data)
result = "success"
} catch (Throwable e) {
result = "failed"
} finally {
log.info("Exit Test Case : {}", name)
}
}
response.failure = { resp ->
println "Unexpected error: ${resp.statusLine.statusCode} : ${resp.statusLine.reasonPhrase}"
def errorInfo = """
Call error: ${resp.statusLine.statusCode} : ${resp.statusLine.reasonPhrase}
Name: ${name}
Url: ${url}
Path: ${path}
Param: ${param}
"""
log.warn(errorInfo)
result = "failed"
}
}
result
}
}

客户端运行测试用例集合:

package cc.lovesq.study.testcase

import cc.lovesq.study.testcase.qa.DetailTestCases
import cc.lovesq.study.testcase.qa.SearchTestCases class ClientTest { def static main(args) { CaseExecutor caseExecutor = new CaseExecutor() caseExecutor.invokeAllCases(new SearchTestCases())
caseExecutor.invokeAllCases(new DetailTestCases()) }
}

代码讲解###

  • 使用了HttpBuilder 类来发送HTTP请求。需要添加POM配置:
<dependency>
<groupId>org.codehaus.groovy.modules.http-builder</groupId>
<artifactId>http-builder</artifactId>
<version>0.6</version>
</dependency>
  • 使用注解 @Case 来表示测试用例。注解可以用于标识一类对象。

  • 使用Groovy闭包来传递校验逻辑。闭包可以用来传递变化的逻辑。

自动加载用例集合###

实际应用中,往往不会直接 new 一个 XXXTestCases 对象,而是将这些对象标记为 Component 后,在应用启动时加载这些 TestCases,建立映射。只要对 CaseExecutor 做一些扩展即可。 如下代码所示:

@Component("caseExecutor")
class CaseExecutor implements ApplicationContextAware { static Logger log = LoggerFactory.getLogger(CaseExecutor.class) ApplicationContext context def casePathMap = [:] @PostConstruct
def init() {
Map<String, Object> components = context.getBeansWithAnnotation(Component.class)
log.info("{}", components)
components.each {
name, comp ->
try {
def property = comp.metaClass.getProperty(comp, 'path')
if ( property) {
casePathMap[property] = comp
}
} catch (e) {
log.warn("not having restPath, omit")
} }
} // other codes @Override
void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext
}
}

小结###

本文讲解了一种基于Groovy+HttpRestful的超轻量级的接口测试用例配置的设计方案及DEMO实现。基于这种方法,可以配置化地快速增加指定服务接口的测试用例集合,而不需要额外编写冗余的测试代码。