使用Groovy+Spock构建可配置的订单搜索接口测试用例集
概述###
测试是软件成功上线的安全网。基本的测试包含单元测试、接口测试。在 “使用Groovy+Spock轻松写出更简洁的单测” 一文中已经讨论了使用GroovySpock编写简洁的单测,本文讲解使用Groovy+Spock来构建订单搜索的接口测试用例集合。
主工程是用Java写的。之所以采用Groovy, 是因为其语法近似Python的简洁,可以方便地构造List, Map 及使用闭包方便地遍历这些容器,可以使用元类方便地访问Java类的私有成员。Groovy 是与 Java 系统集成的好伙伴。
接口测试用例遵循“条件-结果检测”模式,即:给定一系列条件,调用接口返回结果,并对结果进行校验。如果结果与条件有关联,那么结果检测需要将条件与结果关联起来。接口测试用例必须是自动化可重复执行的。
对于订单搜索来说,给定搜索条件 (CA=a, CB=b, CC=c, ......) ,调用订单搜索接口返回的订单列表中,应该校验每一个订单满足 (RA=a, RB=b, RC=c, ......)。
出于对企业源代码的保护,这里业务敏感的信息和代码会略去。 仅保留与 groovy 与 可配置化相关的内容,不影响读者理解其中的技巧。
思路及实现###
思路####
这里的关键点在于: 如何将订单搜索条件与搜索结果的关联做成可配置化的。 如果需要添加测试用例,只要增加新的配置项,而无需更改测试代码。
订单搜索条件,是设置 OrderSearchParam 的字段,比如 orderTypeDesc, receiverName ; 返回结果 SearchResultModel 包含一个 map[field, value] 和一个 total , map 包含每个订单对应的字段 order_type, receiver_name 。 显然,可以构建一个三元组 basicFieldsSearchConfig: [attributeInOrderSearchParam, returnedFieldName, testValue] 。搜索条件设置 attributeInOrderSearchParam = testValue , 然后从返回结果 map 中取出 returnedFieldName 字段,验证其值为 testValue。 搜索设置条件 attributeInOrderSearchParam = testValue, 用到了 Groovy 元类的能力。
类 OrderSearchParam 中含有订单搜索的各种条件字段。
@Data
@ToString(callSuper = true)
public class OrderSearchParam extends BaseParam {
private static final long serialVersionUID = 4096875864279002497L;
/** 订单编号 */
private String orderNo;
/**
* 订单类型
*
* 元素取自
* @see xxx.api.constants.OrderType 枚举的 name()
* eg. NORMAL
*/
private List<String> orderTypeDesc;
/** 收货人姓名 */
private String receiverName;
// ...
public OrderSearchParam() {}
}
代码实现####
import org.junit.Test
import javax.annotation.Resource
class GeneralOrderSearchServiceTest extends GroovyTest {
@Resource
private GeneralOrderSearchService generalOrderSearchService
@Test
void "testSearchOrderType"() {
expect:
OrderType.values().each {
GeneralOrderSearchParam orderSearchParam = ParamUtil.buildGeneralOrderSearchParam(55)
orderSearchParam.getOrderSearchParam().setOrderTypeDesc([it.name()])
PlainResult<SearchResultModel> searchResult = generalOrderSearchService.search(orderSearchParam)
assertSearchResult(searchResult, 'order_type', it.value, orderSearchParam)
}
}
def basicFieldsSearchConfig = [
['orderNo', 'order_no', 'E201805072005xxxxxxxxx1'],
['receiverName', 'receiver_name', 'qinshu'],
// ... other test cases
]
@Test
void "testSearchBasicFields"() {
expect:
basicFieldsSearchConfig.each {
commonGeneralOrderSearchTest(it[0], it[1], it[2])
}
}
void commonGeneralOrderSearchTest(searchParamAttr, returnField, testValue) {
GeneralOrderSearchParam orderSearchParam = ParamUtil.buildGeneralOrderSearchParam(55)
OrderSearchParam searchParam = orderSearchParam.getOrderSearchParam()
searchParam.metaClass.setProperty(searchParam, searchParamAttr, testValue)
PlainResult<SearchResultModel> searchResult = generalOrderSearchService.search(orderSearchParam)
assertSearchResult(searchResult, returnField, testValue, orderSearchParam)
}
void assertSearchResult(searchResult, field, testValue, orderSearchParam) {
if (searchResult == null || searchResult.data == null || searchResult.data.total == 0) {
println(orderSearchParam)
return false
}
assertSuccess(searchResult)
SearchResultModel data = searchResult.getData()
// 通常情况下,必须保证搜索结果非空,搜索用例才有意义
assert data.total > 0
data.records.each {
rec ->
rec.get(field) == testValue
}
}
}
联合搜索####
上面的测试用例仅考虑了单个搜索条件的情形。 现在考虑下多个搜索条件。 单个搜索条件,使用了三个元素的列表 [attributeInOrderSearchParam, returnedFieldName, testValue] 来表示;多个搜索条件,尽管可以采用列表的列表,但是不够直观。可以抽象成一个对象。此外,条件值与返回值不一定是相同的,需要分离出来。 单个搜索抽象为类 SingleSearchTestCase:
class SingleSearchTestCase {
String searchParamAttr
String returnField
Object condValue
Object returnValue
SingleSearchTestCase(String searchParamAttr, String returnField, Object condValue, Object returnValue) {
this.searchParamAttr = searchParamAttr
this.returnField = returnField
this.condValue = condValue
this.returnValue = returnValue
}
}
同时,搜索条件构建和检测结果,也需要扩展成针对多个搜索条件的。 对于单个搜索条件的测试,为了保持兼容,可以做个适配函数。这样,可以使用 List 来表达联合搜索条件的测试用例 (也可以包装为一个含有 List 实例的 CompositeSearchTestCase 对象),贯穿整个条件设置及结果检测。
def combinedFieldsSearchConfig = [
[new SingleSearchTestCase('receiverName', 'receiver_name', 'qinshu', 'qinshu'),new SingleSearchTestCase('orderTypeDesc', 'order_type', 'NORMAL', 0)]
]
@Test
void "testCombinedFieldsSearch"() {
expect:
combinedFieldsSearchConfig.each {
commonGeneralOrderSearchTest(it)
}
}
void commonGeneralOrderSearchTest(searchParamAttr, returnField, testValue) {
commonGeneralOrderSearchTest([new SingleSearchTestCase(searchParamAttr, returnField, testValue, testValue)])
}
void assertSearchResult(searchResult, returnField, returnValue, orderSearchParam) {
assertSearchResult(searchResult, [new SingleSearchTestCase('', returnField, '', returnValue)], orderSearchParam)
}
void commonGeneralOrderSearchTest(List<SingleSearchTestCase> testCase) {
GeneralOrderSearchParam orderSearchParam = ParamUtil.buildGeneralOrderSearchParam(55)
OrderSearchParam searchParam = orderSearchParam.getOrderSearchParam()
testCase.each {
def searchParamAttrType = searchParam.metaClass.getMetaProperty(it.searchParamAttr).type
def condValue = searchParamAttrType.equals(List.class) ? [it.condValue] : it.condValue
searchParam.metaClass.setProperty(searchParam, it.searchParamAttr, condValue)
}
PlainResult<SearchResultModel> searchResult = generalOrderSearchService.search(orderSearchParam)
assertSearchResult(searchResult, testCase, orderSearchParam)
}
void assertSearchResult(searchResult, List<SingleSearchTestCase> testCase, orderSearchParam) {
if (searchResult == null || searchResult.data == null) {
throw new AssertionError("searchParam: " + JSON.toJSONString(orderSearchParam))
}
if (searchResult.data.total == 0) {
appendFile(GlobalConstants.resultFile,
"[NotGood]testCase: " + JSON.toJSONString(orderSearchParam))
assertSuccess(searchResult)
return true
}
assertSuccess(searchResult)
SearchResultModel data = searchResult.getData()
// 通常情况下,必须保证搜索结果非空,搜索用例才有意义
appendFile(GlobalConstants.resultFile, "[Normal]testCase: " + JSON.toJSONString(orderSearchParam) + " total: " + data.total)
assert data.total > 0
data.records.each {
rec ->
testCase.each {
tc -> rec.get(tc.returnField) == tc.returnValue
}
}
}
集成Spock###
在 “使用Groovy+Spock轻松写出更简洁的单测” 一文中,见识了Spock简洁优雅的语法。是否可以在接口测试用例里也使用Spock的优点呢? 只要简单的三步即可。
引入依赖####
要在Spring工程中引入Spock依赖,并访问Spring注入的Bean,需要同时引入 groovy-all, spock-core, spock-spring, spring-test 。如下所示。 低版本的spring-test 可能不支持。
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.7</version>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.9.RELEASE</version>
<scope>test</scope>
</dependency>
启动类####
启动类只要注明 @ContextConfiguration 的 location 即可,不需引入 RunWith。
@ContextConfiguration(locations = "classpath:applicationContext.xml")
class GroovyTest extends Specification {
@Test
void "testEmpty" () {
}
}
使用Spock####
使用Spock语法可将 testSearchBasicFields 改写为:
@Unroll
@Test
void "testSearchBasicFields(#attr,#retField,#testValue)"() {
expect:
commonGeneralOrderSearchTest(attr, retField, testValue)
where:
attr | retField | testValue
'orderNo' | 'order_no' | 'E201805072005xxxxxxxxx1'
'receiverName' | 'receiver_name' | 'qinshu'
}
是不是更清晰明了了 ?
Groovy元类###
测试用例代码中,使用了 Groovy元类 metaClass 。 可以看做是 Java 反射机制的语法形式的简化。 比如可以使用 getProperty 或 setProperty 设置对象属性的值; 可以使用 getMetaProperty 获取对象属性的类型,可以使用 invokeMethod 调用对象的方法等。
setProperty/getProperty 方法在配置化地构造参数对象时很有用;getMetaProperty 在根据实例成员的类型进行判断并采取某种行为时很有用; invokeMethod 方法在java私有方法的单测中很有用。
groovy元编程的介绍可参阅文章: Groovy学习之-运行时元编程
小结###
本文讲解了使用Groovy来构建订单搜索的接口测试用例集合,并介绍了 groovy 元类的用法。 读完本文,是否对 Groovy 的使用有了更深入的了解呢?
使用Groovy+Spock构建可配置的订单搜索接口测试用例集的更多相关文章
- Groovy元编程应用之自动生成订单搜索接口测试用例集
背景 在 "Groovy元编程简明教程" 一文中,简明地介绍了 Groovy 元编程的特性. 那么,元编程可以应用哪些场合呢?元编程通常可以用来自动生成一些相似的模板代码. 在 & ...
- 通用订单搜索的API设计得失录
先把 Joshua Bloch 大神的 API PDF 放在这里膜拜下:"How to Design a Good API and Why it Matters.pdf" 总述 在 ...
- Compile Groovy/Spock with GMavenPlus
在之前的博文里曾使用GMaven插件编译Groovy/Spock,这次使用GMavenplus插件,更加方便. 具体步骤 1. 导入Spock和Groovy依赖 <dependency> ...
- 基于Groovy+HttpRestful的超轻量级的接口测试用例配置的设计方案及DEMO实现
目标 设计一个轻量级测试用例框架,接口测试编写者只需要编写测试用例相关的内容(入参及结果校验),不需要理会系统的实现,不需要写跟测试校验无关的内容. 思路 测试用例分析 一个用例由以下部分组成: (1 ...
- 使用Groovy+Spock轻松写出更简洁的单测
当无法避免做一件事时,那就让它变得更简单. 概述 单测是规范的软件开发流程中的必不可少的环节之一.再伟大的程序员也难以避免自己不犯错,不写出有BUG的程序.单测就是用来检测BUG的.Java阵营中,J ...
- Groovy/Spock 测试导论
Groovy/Spock 测试导论 原文 http://java.dzone.com/articles/intro-so-groovyspock-testing 翻译 hxfirefox 测试对于软件 ...
- Jenkins之构建触发器配置(转载)
构建触发器配置,当你在文本框中输入配置的时间后,文本框下方会有时间解释,这样可以很好的看到自己配置的时间对不对. 可以清晰看到我的配置第一个运行时间是周五上午10点执行,第二次是星期六上午10点. ...
- Android NDK开发 Android Studio使用新的Gradle构建工具配置NDK环境(一)
本文主要讲述了如何如何在Android Studio使用新的Gradle构建工具配置NDK环境,现在把相关的步骤整理出来分享给Android程序员兄弟们,希望给他们在配置NDK环境时带来帮助. 从An ...
- Groovy+Spock单元测试
一.导入依赖 Spock是基于JUnit的单测框架,提供一些更好的语法,结合Groovy语言,可以写出更为简洁的单测. <!-- groovy依赖 --> <dependency&g ...
随机推荐
- 两个java工程之间的相互调用方法
如果你有两个java项目的话,如何向他们之间进行信息的通信前提:必须知道要通信的java项目(接收请求方)的服务器的IP地址和访问路径.其实两个java项目之间的通信还是使用HTTP的请求.主要有两种 ...
- dos2unix命令
dos2unix命令用来将DOS格式的文本文件转换成UNIX格式的(DOS/MAC to UNIX text file format converter).DOS下的文本文件是以\r\n作为断行标志的 ...
- IE无法安装Activex控件
由于无法验证发行者,所以windows已经阻止此软件,如要安装未签名的activex控件,按如下步骤: 1.打开Internet Explorer---菜单栏点“工具”---Internet选项--安 ...
- Java的HashMap是如何实现的?
以下内容转自:http://blog.csdn.net/vking_wang/article/details/14166593 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储 ...
- Spark log4j日志配置详解(转载)
一.spark job日志介绍 spark中提供了log4j的方式记录日志.可以在$SPARK_HOME/conf/下,将 log4j.properties.template 文件copy为 l ...
- [django]详情页列表页
详情页列表页 列表页展示titile--这个模型的部分字段 详情页展示这个模型的所有字段 我想看下related_name这个从主表取子表数据 取数据--官网投票例子 https://docs.dja ...
- save
docker-compose-orderer.yaml # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: ...
- docker容器多服务(不推荐)
1.最常用方式配置进程管理工具Supervisor 2.最简单的就是把多个启动命令放到一个启动脚本里面,启动的时候直接启动这个脚本 第一种方式: 1.构建基础镜像 FROM lmurawsk/pyth ...
- DL中train\dev\test集
转自:https://blog.csdn.net/l8947943/article/details/80328721 training set:训练集是用来训练模型的.遵循训练集大,开发,测试集小的特 ...
- 【EatBook】-NO.3.EatBook.3.JavaArchitecture.2.001-《架构探险:从零开始写Java Web框架》-
1.0.0 Summary Tittle:[EatBook]-NO.3.EatBook.3.JavaArchitecture.2.001-<架构探险:从零开始写Java Web框架>- S ...