You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需重构代码,用Spock/JUnit4测试Groovy时无法mock本地对象如何解决

测试解决方案

前置说明

你当前的代码是Groovy实现,Groovy原生支持构造方法Mock能力,无需修改原有业务代码即可完成全分支覆盖,以下分别提供Spock和JUnit4两种实现方案。

需覆盖的分支梳理

  • 分支1:env.findServiceByName("test-name") 抛出异常,方法直接返回null
  • 分支2:成功获取CfService实例,但service.getMap()中不存在prop.name键,调用toString()触发空指针异常
  • 分支3:成功获取CfService实例,且service.getMap()中存在prop.name对应的值,正常返回字符串结果

Spock 测试实现(推荐,Groovy生态原生适配)

Spock无需额外引入第三方Mock依赖,直接通过构造方法Mock即可实现:

import spock.lang.Specification
import io.pivotal.cfenv.core.CfEnv
import io.pivotal.cfenv.core.CfService

class TestSpec extends Specification {

    def "测试findServiceByName抛出异常时返回null"() {
        given: "Mock CfEnv构造方法,模拟调用findServiceByName抛异常"
        def mockEnv = Mock(CfEnv)
        1 * mockEnv.findServiceByName("test-name") >> { throw new RuntimeException("服务不存在") }
        GroovySpy(CfEnv, global: true) {
            new() >> mockEnv
        }
        def testObj = new Test()

        when: "调用getPropertyValue方法"
        def result = testObj.getPropertyValue()

        then: "验证返回结果为null"
        result == null
    }

    def "测试服务存在但属性不存在时触发空指针"() {
        given: "Mock CfEnv和CfService,返回无对应属性的Map"
        def mockService = Mock(CfService)
        1 * mockService.getMap() >> [:]
        def mockEnv = Mock(CfEnv)
        1 * mockEnv.findServiceByName("test-name") >> mockService
        GroovySpy(CfEnv, global: true) {
            new() >> mockEnv
        }
        def testObj = new Test()

        when: "调用getPropertyValue方法"
        testObj.getPropertyValue()

        then: "验证抛出空指针异常"
        thrown(NullPointerException)
    }

    def "测试正常场景下返回对应属性值"() {
        given: "Mock CfEnv和CfService,返回带目标属性的Map"
        def mockService = Mock(CfService)
        1 * mockService.getMap() >> ["prop.name": "test-value"]
        def mockEnv = Mock(CfEnv)
        1 * mockEnv.findServiceByName("test-name") >> mockService
        GroovySpy(CfEnv, global: true) {
            new() >> mockEnv
        }
        def testObj = new Test()

        when: "调用getPropertyValue方法"
        def result = testObj.getPropertyValue()

        then: "验证返回结果正确"
        result == "test-value"
    }
}

JUnit4 测试实现

JUnit4需要配合PowerMock实现构造方法Mock,需先引入PowerMock相关依赖,测试代码如下:

import org.junit.Test
import org.junit.runner.RunWith
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import io.pivotal.cfenv.core.CfEnv
import io.pivotal.cfenv.core.CfService
import static org.junit.Assert.*
import static org.mockito.ArgumentMatchers.anyString

@RunWith(PowerMockRunner.class)
@PrepareForTest([Test.class, CfEnv.class, CfService.class])
class TestJUnit4Test {

    @Test
    void 测试findServiceByName抛出异常时返回null() {
        // Mock CfEnv构造
        CfEnv mockEnv = PowerMockito.mock(CfEnv.class)
        PowerMockito.whenNew(CfEnv.class).withNoArguments().thenReturn(mockEnv)
        PowerMockito.when(mockEnv.findServiceByName(anyString())).thenThrow(new RuntimeException("服务不存在"))
        Test testObj = new Test()
        
        String result = testObj.getPropertyValue()
        assertNull(result)
    }

    @Test(expected = NullPointerException.class)
    void 测试服务存在但属性不存在时触发空指针() {
        // Mock CfEnv和CfService
        CfService mockService = PowerMockito.mock(CfService.class)
        PowerMockito.when(mockService.getMap()).thenReturn(Collections.emptyMap())
        CfEnv mockEnv = PowerMockito.mock(CfEnv.class)
        PowerMockito.whenNew(CfEnv.class).withNoArguments().thenReturn(mockEnv)
        PowerMockito.when(mockEnv.findServiceByName(anyString())).thenReturn(mockService)
        Test testObj = new Test()
        
        testObj.getPropertyValue()
    }

    @Test
    void 测试正常场景下返回对应属性值() {
        // Mock CfEnv和CfService
        CfService mockService = PowerMockito.mock(CfService.class)
        Map<String, Object> propMap = new HashMap<>()
        propMap.put("prop.name", "test-value")
        PowerMockito.when(mockService.getMap()).thenReturn(propMap)
        CfEnv mockEnv = PowerMockito.mock(CfEnv.class)
        PowerMockito.whenNew(CfEnv.class).withNoArguments().thenReturn(mockEnv)
        PowerMockito.when(mockEnv.findServiceByName(anyString())).thenReturn(mockService)
        Test testObj = new Test()
        
        String result = testObj.getPropertyValue()
        assertEquals("test-value", result)
    }
}

注意:原有代码中实例化的CfCredentials creds未参与任何逻辑计算,无需Mock,不影响测试结果。

内容的提问来源于stack exchange,提问作者Vaishnavi Killekar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 09:39:03