Java中Spock框架Mock对象的方法经验总结

 更新时间:2022年02月23日 14:31:08   作者:FunTester  
这篇文章主要分享了Spock框架Mock对象的方法经验总结,下文分享一些常用项目实战说明以及代码,供大家项目中参考,也具有一的的参考价值,需要的小伙伴可以参考一下

前言:

下面分享一些使用过的一个常用项目,部分信息隐去了。大家在自己项目中实践的时候可以参考,尽量别直接抄代码,我自己使用过程中有很多兼容性的坑,特别是IDE自动import功能。

一、技术方案

本技术方案基于公司力推的Spock单元测试框架,spock是一款基于Groovy语言的单元测试框架,其基础也是Java的Junit,目前最新版已经到了2.0,但对Groovy和相应的Java版本要求较高,所以Groovy版本使用1.+,Spock自带的Mock和Spy足够好了,对于对象行为的模拟满足绝大部分场景,但是涉及静态方法模拟时候存在局限性,所以引入MockitoPowerMock来实现设计静态方法的测试模拟场景。

以下为相关依赖版本:     

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

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>1.7.4</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>1.7.4</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.8.9</version>
            <scope>test</scope>
        </dependency>

因为Spock本身需要Groovy语言支持,所以也需要一个Groovy-all的依赖,请注意注自带的注释:

      <dependency> <!-- use a specific Groovy version rather than the one specified by spock-core -->
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.7</version>
        </dependency>

另外,提供的配置文件中多了几项特殊场景下使用的依赖,提供参考:

   <dependency> <!-- enables mocking of classes (in addition to interfaces) -->
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.9.9</version>
            <scope>test</scope>
        </dependency>
        <dependency> <!-- enables mocking of classes without default constructor (together with CGLIB) -->
            <groupId>org.objenesis</groupId>
            <artifactId>objenesis</artifactId>
            <version>3.0.1</version>
            <scope>test</scope>
        </dependency>
        <dependency> <!-- only required if Hamcrest matchers are used -->
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <version>2.1</version>
            <scope>test</scope>
        </dependency>

二、非静态资源

由于多个单测框架的方法名重复较多,我把import内容也贴出来了,如果同样的代码无法运行,可以排查一下是否import正确的方法和类。这里不是很建议import static ,因为可能出现混用以及不易排查的问题。

由于目前测试中没有遇到使用Spy放行的逻辑,所以均使用Mock模式,需要对Mock对象的方法进行模拟。这个分为两类:Spock和PowerMock(结合Mockito)。原因是在混合静态资源和非静态资源场景下,指定了PowerMock@RunWith运行规则,不兼容Spock写法,需要用到PowerMock框架Mock对象的功能。

三、Mock被测对象

1.@Autowired构造方法

用一个controller举例,源代码如下:

@Api(tags = "SLA规则管理模块")
@Slf4j
@RestController
@RequestMapping("/hickwall/v1/static/sla")
public class FunController {

    HttpServletRequest request;

    ISlaService service;

    @Autowired
    public FunController(HttpServletRequest request, ISlaService service) {
        this.request = request;
        this.service = service;
    }

}

Spock单测代码如下:

import com.funtester.service.ISlaService
import com.funtester.vo.sla.SlaBean
import spock.lang.Shared
import spock.lang.Specification

import javax.servlet.http.HttpServletRequest

class FunControllerTest extends Specification {

    def service = Mock(ISlaService)

    @Shared
    def request = Mock(HttpServletRequest)
    
    def FunController = new FunController(request, service)

}

2.@Autowired属性对象,无构造方法

源代码如下:

public class ApiImpl implements IApi {

    @Autowired
    private ApiRMapper mapper;
}

Spock单测部分代码如下:

import com.funtester.mapper.ApiRMapper
import com.funtester.vo.ApiR
import spock.lang.Shared
import spock.lang.Specification


    ApiRMapper mapper = Mock(ApiRMapper)

    def drive = new ApiImpl(mapper:mapper)

3.PowerMock用法

场景也分为两种:有无构造方法,除了Mock方法不同以外,其他均相同,这里不列举。

PS:如果对象属性中有未被@Autowired注释的属性,不能用@AllArgsConstructor的lombok注解,服务启动会报错。

源代码如下:

@Component
@Slf4j
public class TaskScheduled {

    @Autowired
    IService service;
    
    @Value("${hickwall.statistic.cid}")
    public  String cid;

}

4.共享对象以及初始化

统一使用Spock提供的功能,用到的注解@Shared,不加的话无法在Spock方法中进行赋值操作,但是可以当做一个普通的对象使用。

Spock框架Demo:

    @Shared
    def slaBean = new SlaBean()

    def setupSpec() {
        request.getHeader("operator") >> "FunTester"
        slaBean.name = "测试"
        slaBean.quota = 1
        slaBean.upstream = "PRO"
        slaBean.threshold = 0.1
        slaBean.creator = "FunTester"
        slaBean.id = 100
    }

四、定义对象行为

1.Spock定义Mock对象行为

基础的Spock语法结构when-then-expct如下:

    def "AddSla"() {
        when:
        def sla = FunController.addSla(slaBean)
        then:
        service.addSla(_) >> {f ->
            assert "FunTester" in f.creator
            1
        }

        expect:

        sla.code == 0
        sla.data == 1

    }

也可以在最开始加上givenwhenthen通常一起使用。

上述Demo在Mock方法的时候对参数进行了断言和处理,这也是Spock框架的一个特性,其他均为Groovy语法特性。

其他定义Mock行为的语法如下:

   service.getAllGroup(_,_) >> null//返回null
        service.getAllGroup(_,_) >> {throw new Exception()} //抛出异常
        service.getAllGroup(_,_) >> []//返回空list,Groovy默认实现ArrayList
        service.getAllGroup(_,_) >> [slaBean,slaBean]//返回正常list
        service.getAllGroup(_,_) >> [slaBean,slaBean]//返回正常list
        service.getAllGroup(_,10) >> [slaBean,slaBean]//定时某个参数
        service.getAllGroup(any(),10) >> [slaBean,slaBean]//any()等效于_
        service.getAllGroup(any(),10) >> service.getAllGroup(1,10)//调用其他方法返回

2.Mockito模拟对象行为

MockitoPowerMock配合使用语法稍微复杂一些。首先我们需要先定义对象行为(通常在com.funtesterbase.task.TaskScheduledTest#setupSpec方法中),然后在用例用使用。

定时对象行为:

        Mockito.when(newutil.filter(Mockito.any())).thenReturn(true)

定义行为以后,就可以在Spock用例中正常使用,包括在通过Mock对象创建的对象方法中,如果调用到定义过行为的方法,也会走自定义的逻辑。

其他常用定义行为:

        Mockito.when(newutil.filter(Mockito.any())).thenReturn(null)
        Mockito.when(newutil.filter(Mockito.any())).thenThrow(Exception.class)//抛出异常
        PowerMockito.doNothing().when(newutil).filter(Mockito.any(ArrayList.class))//dothing,什么都不做

第三个例子中我们假设filter方法是一个无返回的void方法。

通常我们需要构建返回对象,如果对象需要赋值的属性过多,可以使用初始化赋值的方法,下面是Mock一个返回list的方法返回值的Demo:

Mockito.when(newser.selectAllService()).thenReturn([new NewInterface() {

            {
                setUrl("/abc")
                setNname("test TGFC z;/./")
                setMethod("GET")
            }
        }, new NewInterface() {

            {
                setUrl("/abcd")
                setNname("test")
                setMethod("POST")
            }
        }, new NewInterface() {

            {
                setUrl("/abce")
                setNname("test")
                setMethod("GET")
            }
        }])

到此这篇关于Spock框架Mock对象的方法经验总结的文章就介绍到这了,更多相关Spock框架Mock对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java实现学生信息管理系统

    java实现学生信息管理系统

    这篇文章主要为大家详细介绍了java实现学生信息管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Java中覆盖finalize()方法实例代码

    Java中覆盖finalize()方法实例代码

    这篇文章主要介绍了Java中覆盖finalize()方法实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • RocketMQ Broker消息如何刷盘源码解析

    RocketMQ Broker消息如何刷盘源码解析

    这篇文章主要为大家介绍了RocketMQ Broker消息如何刷盘源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • 一篇文章带你深入了解Java基础(4)

    一篇文章带你深入了解Java基础(4)

    这篇文章主要给大家介绍了关于Java中方法使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • 浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题

    浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题

    本篇文章主要介绍了浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题,具有一定的参考价值,有需要的可以了解一下
    2017-08-08
  • Java实现SHA-256加密算法的完全解析

    Java实现SHA-256加密算法的完全解析

    SHA-256是一种散列(哈希)算法,用于将任意长度的数据映射为固定长度的散列值,以保证数据完整性。本文将为大家介绍一下SHA-256加密算法的原理与实现,希望对大家有所帮助
    2023-02-02
  • JDK、J2EE、J2SE、J2ME四个易混淆概念区分

    JDK、J2EE、J2SE、J2ME四个易混淆概念区分

    这篇文章将向你详细介绍JDK、J2EE、J2SE、J2ME的概念以及他们的关系区别。
    2015-09-09
  • Java源码重读之ConcurrentHashMap详解

    Java源码重读之ConcurrentHashMap详解

    ConcurrentHashMap(CHM)是日常开发中使用频率非常高的一种数据结构。本文将从源码角度带大家深入了解一下ConcurrentHashMap的使用,需要的可以收藏一下
    2023-05-05
  • Spring + ECharts实现数据可视化的案例详解

    Spring + ECharts实现数据可视化的案例详解

    Apache ECharts是一个基于 JavaScript 的开源可视化图表库,在网页上实现数据的可视化,非常好用,本文将通过一个简单的demo来给大家介绍一下Spring + ECharts如何数据可视化,需要的朋友可以参考下
    2023-07-07
  • Java创建线程池的方式实现

    Java创建线程池的方式实现

    本文主要介绍了创建线程池的方式,包括三种方式,第一种是使用Executors框架,第二种是使用ThreadPoolExecutor,第三种是使用ForkJoinPool,感兴趣的可以了解一下
    2024-12-12

最新评论