在实际使用Spock的过程中如何把一些常用的测试方法抽出来,封装成基类使用。
BaseSpock
在前面几篇文章讲解Spock结合Power Mock实现静态方法mock功能时,示例代码里经常会用到LogUtils等工具类的静态方法去记录日志,那我们就可以把LogUtils类的mock代码抽到一个公共类中,然后我们的测试类去继承我们自己实现的公共类
比如我们把公共类起名叫BaseSpock.groovy文件,那么继承它的子类就拥有了模拟LogUtils静态方法的功能,而不用每个测试类单独去实现mock LogUtils日志的功能
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.spock.example
import com.spock.example.utils.LogUtils import org.junit.runner.RunWith import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor import org.powermock.modules.junit4.PowerMockRunner import org.powermock.modules.junit4.PowerMockRunnerDelegate import org.spockframework.runtime.Sputnik import spock.lang.Specification
@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(Sputnik.class) @PrepareForTest([LogUtils.class]) @SuppressStaticInitializationFor(["com.spock.example.utils.LogUtils"]) class BaseSpock extends Specification { void setup() { println "Spock setup" PowerMockito.mockStatic(LogUtils.class) } }
|
BaseSpock是我们封装的spock基类,它继承Specification,在setUp方法内部对LogUtils进行了mock
BaseSpock可以放在一个公共的项目中或作为jar的方式引用,也可以放在src/main/groovy/下面作为一个公共类调用

然后原来用到LogUtils日志类的单元测试可以继承BaseSpock基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| package com.spock.example.service
import com.spock.example.BaseSpock import com.spock.example.dao.UserDAO import com.spock.example.dto.UserDTO import com.spock.example.utils.IDNumberUtils import org.mockito.Mockito import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest
@PrepareForTest([IDNumberUtils.class]) class UserServiceStaticTest extends BaseSpock {
def processor = new UserService() def dao = Mock(UserDAO)
void setup() { processor.userDAO = dao PowerMockito.mockStatic(IDNumberUtils.class) }
def "GetUserByIdStatic"() { given: "设置请求参数" def user1 = new UserDTO(id: 1, name: "张三", province: "上海") def user2 = new UserDTO(id: 2, name: "李四", province: "江苏") def idMap = ["birthday": "1992-09-18", "sex": "男", "age": "28"]
and: "mock掉接口返回的用户信息" dao.getUserInfo() >> [user1, user2]
and: "mock静态方法返回值" PowerMockito.when(IDNumberUtils.getBirAgeSex(Mockito.any())).thenReturn(idMap)
when: "调用获取用户信息方法" def response = processor.getUserByIdStatic(1)
then: "验证返回结果是否符合预期值" with(response) { name == "张三" abbreviation == "沪" postCode == 200000 age == 28 } } }
|
如果你除了LogUtils这些常用的类需要mock外,还需要mock其他的静态方法的话,使用前面介绍的spock结合power mock的用法即可,类似下面这样写:

当前的单元测试类需要mock IDNumberUtils类,可以使用@PrepareForTest注解,这样既可以使用基类mock LogUtils日志的功能,也可以给自己的单测类增加新的静态方法mock功能
注意事项
- BaseSpock的类型是groovy文件,这个是因为Spock内置的测试引擎在启动时会检查继承它的子类是否是groovy类型的文件,所以如果你要封装一个类似BaseSpock的基类,文件后缀不能是 .java的,必须是 .groovy的类型
- 所有的Spock单测类不能有自己的构造函数,因为单元测试的实例都是由Spock创建和管理的
Spock单元测试代码的运行顺序是:
setupSpec() → setup() → cleanup() → cleanupSpec()
Spock中的代码块和JUnit对应关系
Spock |
JUnit |
Specification |
Test class |
setup() |
@Before |
cleanup() |
@After |
setupSpec() |
@BeforeClass |
cleanupSpec() |
@AfterClass |
Feature |
Test |
Feature method |
Test method |
Data-driven feature |
Theory |
Condition |
Assertion |
Exception condition |
@Test(expected=…) |
Interaction |
Mock expectation(e.g. in Mockito) |