Spock框架Mock对象、方法经验总结

Spock框架Mock对象、方法经验总结,第1张

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


近期已然陷入了单元测试的汪洋大海,上万行的代码突然要求起来单元测试覆盖率,着实很恐怖的。最经过艰苦的抗争学习之后,终于迈过了技术这个坎儿,特来分享一下最近踩坑的经历,和一些典型的使用场景案例分享。

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

技术方案

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

以下为相关依赖版本:


    org.spockframework
    spock-core
    1.2-groovy-2.5
    test


    org.spockframework
    spock-spring
    1.2-groovy-2.5
    test



    org.powermock
    powermock-api-mockito2
    1.7.4
    test



    org.powermock
    powermock-module-junit4
    1.7.4
    test



    org.mockito
    mockito-core
    2.8.9
    test

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

 
    org.codehaus.groovy
    groovy-all
    2.4.7

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

 
    net.bytebuddy
    byte-buddy
    1.9.9
    test

 
    org.objenesis
    objenesis
    3.0.1
    test

 
    org.hamcrest
    hamcrest-core
    2.1
    test

非静态资源

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

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

Mock被测对象 @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)

}
@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)
PowerMock用法

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

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

源代码如下:

@Component
@Slf4j
public class TaskScheduled {

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

}
共享对象以及初始化

统一使用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
}
定义对象行为 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

}

也可以在最开始加上given,when和then通常一起使用。

上述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)//调用其他方法返回
Mockito模拟对象行为

Mockito和PowerMock配合使用语法稍微复杂一些。首先我们需要先定义对象行为(通常在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")
                setMethod("GET")
            }
        }, new NewInterface() {

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

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

技术行业要不断地学习,学习肯定不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)

送给大家一句话,共勉:当我们能力不足的时候,首先要做的是内修!当我们能力足够强大的时候,就可以外寻了!

最后也为大家准备了一份配套的学习资源,你可以微信扫描下方二维码,免费获取一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中资料包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!


好文推荐

转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!

面试经:一线城市搬砖!又面软件测试岗,5000就知足了…

面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…

什么样的人适合从事软件测试工作?

那个准点下班的人,比我先升职了…

测试岗反复跳槽,跳着跳着就跳没了…

包装成1年工作经验的测试工程师,我给他的面试前的建议如下

“入职一年,那个被高薪挖来的自动化软件测试被劝退了。”

4个月自学软件测试面进阿里!如何从功能测试转成自动化…我经历了什么

6000元报了培训班,3个月后我成功“骗”进了腾讯大厂,月薪15000

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5708396.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存