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

使用Spock Mock Spring Repository注解类时返回null的原因探究

解决Spock Mock Spring @Repository Bean返回Null的问题

这是个挺常见的Spring与Spock整合的小坑,我来帮你拆解问题原因和可行的解决方案:

问题现象回顾

  • 当自定义的TestRepository标注@Repository注解时,通过Spock DetachedMockFactory创建的Mock注入后,调用方法始终返回null
  • 但将注解替换为@Component、@Service或@Controller时,Mock能正常返回预期的TestModel实例

核心原因分析

Spring对标注@Repository的Bean有特殊的自动处理逻辑:它会为这类Bean自动生成代理类,主要用于统一转换持久层抛出的异常(比如将JDBC异常转为Spring的DataAccessException)。这个代理会包裹你创建的Spock Mock,导致你设置的方法交互(testRepository.getTestModel >> new TestModel())无法被正确触发,最终返回默认的null。

而@Component、@Service等其他 stereotype 注解不会触发这个自动代理逻辑,所以Mock能正常工作。

可行解决方案

方案1:使用Spock的@SpringBean注解(推荐)

Spock的Spring模块提供了@SpringBean注解,专门用于在Spring上下文里替换目标Bean为Spock Mock,它能自动处理Spring的代理问题,代码更简洁:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc(secure = false)
@DirtiesContext
class TestControllerSpec extends Specification {
  @Autowired
  MockMvc mockMvc

  // 直接用@SpringBean定义Mock,无需手动编写@TestConfiguration
  @SpringBean
  TestRepository testRepository = Mock()

  def "test endpoint returns expected model"() {
    given:
    // 正常设置Mock交互
    testRepository.getTestModel() >> new TestModel()

    when:
    def result = mockMvc.perform(get("/test"))

    then:
    // 按需添加断言
    result.status.isOk()
  }
}

方案2:禁用Repository的自动代理

如果你的测试不需要其他Spring Data Repository的功能,可以通过配置属性关闭Repository的自动代理:

@SpringBootTest(
  webEnvironment = SpringBootTest.WebEnvironment.MOCK,
  properties = ["spring.data.jpa.repositories.enabled=false"]
)
@AutoConfigureMockMvc(secure = false)
@DirtiesContext
class TestControllerSpec extends Specification {
  // 保留原有的Mock配置即可
  @Autowired
  MockMvc mockMvc
  @Autowired
  TestRepository testRepository

  def "test"() {
    testRepository.getTestModel() >> new TestModel()
    // 测试逻辑...
  }

  @TestConfiguration
  static class MockConfig {
    def factory = new DetachedMockFactory()
    @Bean
    @Primary
    TestRepository testRepository() {
      factory.Mock(TestRepository)
    }
  }
}

注意:这个方法会全局禁用Spring Data Repository的自动配置,如果测试中涉及其他真实Repository,可能会影响功能。

方案3:给自定义Repository添加@NoRepositoryBean

如果你的TestRepository不是真正的Spring Data Repository(只是自定义的持久层工具类),可以添加@NoRepositoryBean告诉Spring不要将其视为持久层Repository,从而避免自动代理:

@Repository
@NoRepositoryBean
class TestRepository {
  TestModel getTestModel() {
    // 原有逻辑
  }
}

这样原有的Mock配置就能正常生效了。


内容的提问来源于stack exchange,提问作者Patrick Roos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:18:36