Spock 单元测试实践中的一些问题1

4,710 阅读4分钟

被测试方法存在多种场景时,该如何组织?

一般方法是:一个被测方法对应一个测试方法。当场景存在多个时,使用 whenthen 块进行分割

def "test_method"() {
    when: "用例场景1 描述"
    // do something
    then: ""
    // verify something
    /* ---------------------------------------------------------------------------- */
    
    when: "用例场景2 描述"
    // do something
    then: ""
    // verify something
}

为什么在 when 代码块中,不能使用泛指匹配符,在 then 代码中却可以

现有如下代码:

def "test_settleBind"() {
    // BindSettleRelationReq relationReq = new BindSettleRelationReq()
    when: "request bean channelcode is blank"
    BindSettleRelationResp resp = channelService.settleBind(*_)
    then:
    resp.status == "0"
}

指的是 settleBind 方法,不关心传入的参数,返回值的 status 值永远是字符串 0。但当运行时会报错:

groovy.lang.MissingMethodException: No signature of method: com.fingard.rh.rhf.webapi.app.service.impl.remote.WebapiPayChannelServiceImpl.settleBind() is applicable for argument types: (org.spockframework.lang.SpreadWildcard) values: [*_]

意思为方法参数不匹配。

本质原因是,此时 when 块中使用的是实际的对象(new 出来的)而不是由 Spock Mock 出来的,所以当使用匹配符时,都会报参数类型不匹配。

Spock 是否且如何对私有方法进行单测?

看到了好几篇,关于要不要对私有方法进行单元测试的,热门答案是:私有方法不应该具有复杂的逻辑,只需在对调用私有方法的公开方法,覆盖到即可。如果私有方法逻辑非常复杂,那么此时就应该单独抽类来承担这部分功能。

You generally don't unit test private methods directly. Since they are private, consider them an implementation detail. Nobody is ever going to call one of them and expect it to work a particular way.

You should instead test your public interface. If the methods that call your private methods are working as you expect, you then assume by extension that your private methods are working correctly.

但也有持相反观点的,现实情况确实存在逻辑很复杂,但因为类层次和访问权限问题(只有某个类中调用)不该单独抽类的。

I disagree. Sometimes a private method is just an implementation detail, but it still is complex enough that it warrants testing, to make sure it works right. The public interface may be offering a too high level of abstraction to write a test which directly targets this particular algorithm. It's not always feasible to factor it out into a separate class, for due to the shared data. In this case I'd say it's OK to test a private method

本人持的观点是,私有方法应该测试,且可以测试的很优雅。因为 Groovy 语言 本身就支持直接访问私有方法,与访问 public 方法并无异,所以并不需要通过反射、修改访问权限、将测试类与被测试类放统一包下等不优雅的手段来达到单测效果。

反倒觉得,问题应该是:“是否且如何优雅地 Mock 私有方法?”

下面是几篇关于私有方法单测问题的讨论:

Spock 如何 Mock private、构造(new)?

Spock 不支持 Mock private、构造、静态方法。常见解决方法是引入 mockito 等支持高级 Mock 的框架。

但是两者的兼容性不是特别好,每次运行单元测试时都会在控制台输出如下提示:

Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST

且引入又一个测试框架后,单测代码中会同时存在几种风格的代码(groovy、Spock、Mockito)可读性大大下降。

所以目前,当需要 Mock private、static、new 操作的场景,都想办法,改代码来避免。

Spock 如何验证抛出的异常?

def "test_queryResultAndUpdate"() {
    when: "channelCode is not exist"
    TransRecordPo record1 = new TransRecordPo(extend: "{\"fgMchId\":\"11\"}", state: getRandomValue(TransOrderStatus.NEED_QUERY_STATES))
    BizResult<QueryTransOrderResp> bizResult1 = channelProxyManager.queryResultAndUpdate(record1)

    then:
    FailException e = thrown(FailException.class)
    e.getMessage() == "支付渠道类型映射失败"
}

Spock 官网上说了支持 Mock static 方法,但实际却不起作用?

官网描述:spockframework.org/spock/docs/…

简单来说,Spock 的静态方法 Mock 只能作用于 .groovy 文件,对 .java 不起作用。

参考:blog.csdn.net/paincupid/a…

Spock 如何 Mock 部分方法(Partial Mocks)?

使用 Spy 来创建只需要 部分 Mock 的对象。如下实例所示,ChannelProxyManager#jsApiPay 是被测试方法,在该方法中,调用了 ChannelProxyManager#aliJsApiPay 方法。但此时需要测试的场景与 aliJsApiPay 无关,此时就需要用到 Spy

注:被部分 Mock 的方法,必须为 public

def "test_jsApiPay"() {
    ChannelProxyManager spyManager = Spy(ChannelProxyManager,
            constructorArgs: [recordManager, fgMchChannelInfoCacher, serviceConfig, payInfoCacher, weChatPayHelper]) {
    } as ChannelProxyManager
    String payInfo = String.valueOf(System.currentTimeMillis())
    
    when: "ali jsapi order success"
    PayBo payBo2 = new PayBo(fgTransType: FGTransType.ALI_FWC_PAY, payChannelType: PayChannelType.ALI)
    BizResult<String> result2 = spyManager.jsApiPay(payBo2)
    println JacksonUtil.object2Json(result2)
    
    then:
    1 * spyManager.aliJsApiPay(*_) >> BizResult.success(payInfo)
    result2.success
    result2.getData() == payInfo
}

如何打印 block desc

stackoverflow.com/questions/4…

与 PowerMock 结合时,如何验证静态静态方法调用次数

www.coder.work/article/134…