如何简单的使用Groovy+Spock写单测用例

概述

  软件测试是软件开发中必不可少的流程之一,但是软件测试又全部都是测试人员的工作,作为开发人员最好也承担其中的一部分工作,因为开发人员了解自己的功能需要覆盖哪些必要的场景,而测试人员是帮你找到你没有覆盖到的场景。而且写单测用例能够有效的帮助项目做CI与DI。所以,既然是一件不可避免的事,我们何不让其变得简单呢。

依赖与基础

本人的项目环境如下:
JDK8,Spring Boot 2.2.0.RELEASE
要使用Groovy+Spock编写单测,首先引入如下Maven依赖,同时安装Groovy插件。

<dependency>
     <groupId>org.spockframework</groupId>
     <artifactId>spock-core</artifactId>
     <version>1.2-groovy-2.4</version>
     <scope>test</scope>
 </dependency>

 <dependency>
     <groupId>org.spockframework</groupId>
     <artifactId>spock-spring</artifactId>
     <version>1.2-groovy-2.4</version>
     <scope>test</scope>
 </dependency>

接下来我们来讲一下Groovy的常见语法,作为一门动态语言,它的写法比Java简单很多,而且基本只要你会写Java,你很容易就可以上手Groovy。
首先看一下测试模板方法的定义和JUnit的对比:
在这里插入图片描述
Spock的模板方法说明:

def setupSpec() {}    // runs once -  before the first feature method
def setup() {}        // runs before every feature method
def cleanup() {}      // runs after every feature method
def cleanupSpec() {}  // runs once -  after the last feature method

常见用法

expect-where

有时,里面的某个测试用例失败了,却难以查到是哪个失败了。这时候,可以使用Unroll注解,该注解会将where子句的每个测试用例转化为一个 @Test 独立测试方法来执行,这样就很容易找到错误的用例。 方法名还可以更可读些。

@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class StaffQueryServiceSpec extends Specification {
	@Autowired
	private StaffQueryService staffQueryService;
	
	@Unroll
	def "通过归属人ID查询员工"(){
	expect:
	staffQueryService.getStaffById(ownerId)!=null
	where:
	ownerId | _
	1 | _
	2 | _
	}
}

where后面的条件可以通过表格的形式或者数据管道的形式,单个数据的时候可以通过“_”构建表格

def "通过归属人ID查询员工"(){
expect:
staffQueryService.getStaffById(ownerId)!=null
where:
ownerId << [1,2,3]
}
given-expect-where
@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class OrderQueryServiceSpec extends Specification {
	@Autowired
	private OrderQueryService orderQueryService;
	
@Unroll
 def "根据经销商机构判断是否存在在途订单"() {
	  given:
	  def orgId = 1
	  def dealerId = dealer
	  expect:
	  orderQueryService.hasOnWayOrder(dealerId, orgId) == result
	  where:
	  dealer | result
	  1        | true
	  9999     | false
 }
} 
given-when-then

这个比较符合敏捷开发中用户故事的描述,也是用的最多的一种写法。

@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class OrderAppServiceSpec extends Specification {
	@Autowired
	private OrderAppService orderAppService;
	
def "取消订单"() {
        given:
        def dto = new CancelOrderDTO(operatorId: "1", operatorName: "tzx", dealerId: 1)
        when:
        dto.setOrderNo("OR202001020014")
        orderAppService.cancelOrder(dto)
        then:
        noExceptionThrown()
        when:
        dto.setOrderNo("OR202001020010")
        orderAppService.cancelOrder(dto)
        then:
        thrown(Exception)
    }
}    
given-when-then-where
@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class OrderAppServiceSpec extends Specification {
	@Autowired
	private OrderAppService orderAppService;
	
def "取消订单"() {
        given:
        def dto = new CancelOrderDTO(operatorId: operatorId, operatorName: operatorName, dealerId: dealerId)
        when:
        dto.setOrderNo("OR202001020014")
        orderAppService.cancelOrder(dto)
        then:
        noExceptionThrown()
        where:
        operatorId| operatorName | dealerId
        1| "tzs" | 1
        2| "zhangsan" | 1
        3 | "wangwu" | 2
    }
}    

如果有多个条件需要测试时可以采用这种写法

given-when-then-thrown

在 when 子句中调用了会抛出异常的方法,而在 then 子句中,使用 thrown 接收方法抛出的异常,并赋给指定的变量 ex, 之后就可以对 ex 进行断言了。

@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class CustomerAppServiceSpec extends Specification {
	@Autowired
	private CustomerAppService customerAppService;
	
	def "更新未存在客户" (){
	 given:
	 def dto = new CustomerCreateDTO(customerId: -1,customerName: "小王");
	 when: "客户不存在时"
	 customerAppService.updateCustomer(dto);
	 then:
	 def ex = thrown(ConnectorBusinessException)
	 ex.class.name = "java.lang.RuntimeException"
	 ex.cause.class.name = "com.souche.connector.common.exception.ConnectorBusinessException"
	}
}
打桩

很多时候我们测试的时候不需要执行真正的方法,因为在测试代码里面构建一个真实的对象是比较麻烦的,特别是使用一些依赖注入框架的时候,因此有了打桩的功能。
比如我们依赖某个外部应用的service的返回值,可以采用如下的方式。

@SpringBean
 UserCenterQueryService userCenterQueryService = Mock{
     getUserByShopCodeAndRole(_,_) >> "001"
 };

如果需要定义多次调用返回不同的返回值可以采用下面的方式

@SpringBean
UserCenterQueryService userCenterQueryService = Mock{
    getUserByShopCodeAndRole(_,_)  >>>  ["001","002"]
};

总结

本文介绍了expect-where,given-expect-where,given-when-then,given-when-then-where
given-when-then-thrown,打桩等用法,相信已经适用了绝大多数场景。

参考链接

官方文档
GitHub连接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章