【问题标题】:How to mock a return value of a private method in spock test如何在 spock 测试中模拟私有方法的返回值
【发布时间】:2025-12-06 17:40:01
【问题描述】:

我想测试一个调用另一个私有方法的公共方法,我使用下面的反射方式来获取私有方法并尝试模拟它的返回值,但它没有工作,因为测试停止在私人电话在哪里。有什么建议吗?

Method testMethod = handler.getClass().getDeclaredMethod("test", String.class)
testMethod.setAccessible(true)
testMethod.invoke(handler, "test string") >> true

testMethod 如下所示:

private boolean test(String str) {
    return true;
}

【问题讨论】:

  • 你使用的是哪个模拟框架?
  • 您是否尝试过简单地通过metaClass 替换该方法。最好替换私有方法使用的依赖项,而不是尝试替换该方法。但如果类是 Groovy,您应该能够执行以下操作:instance.metaClass.test = { String s -> return true } 以拦截该实例中的调用。
  • 对不起,我好像错了。我以为我以前用这种方式在 Groovy 中拦截了私人电话。

标签: java unit-testing groovy spock


【解决方案1】:

使用 cglib 代理 Spock 模拟类。这样的代理不能模拟最终类或私有方法(因为私有方法是隐式最终的)。如果您的测试代码是用 Groovy 编写的(例如脚本或 grails 应用程序),那么您可以使用 Spock GroovyMock 或修补元类:

setup:
  HandlerClass.metaClass.test = { true }

given: "a handler"
  def handler = new HandlerClass()

when: "i call test" 
  def r = handler.test()

then:
  r == true

但是,您可能应该更多地关注代码的可测试性。必须模拟类通常不是代码的可维护性和可测试性的好兆头...

【讨论】:

  • 我可以把所有这些步骤都放在'given'中吗?我试过了,但似乎没有用
  • 我使用 HandlerClass.metaClass.test = { String str -> true } 覆盖了方法,但仍然没有运气,覆盖功能是否不适用于私有方法?
  • 它仅适用于 groovy 代码,不适用于 java。只有 groovy 知道元类。如果你调用java代码,那么元类将被忽略
  • 好的,有什么建议吗?我认为这应该是非常正常的使用 Groovy 测试 Java 代码,对吧?
  • 我唯一的建议是改进您的代码以使其可测试;-) 接口背后的抽象组件行为,使用内存数据库的集成测试等。
【解决方案2】:

您不能使用 Mockito 模拟私有方法。但如果有明确的需求,您可以尝试查看 PowerMock。

不要为私有方法编写测试,因为它们会在您为公共方法编写测试时被覆盖。

如果您的任何模拟在私有方法中被调用,那么您可以通过执行以下操作来验证调用:

Mockito.verify(myMock, Mockito.times(1)).myMethod(myParams,...,...);

【讨论】:

  • 谢谢,但情况是我可能不得不模拟该私有方法,因为它在运行时调用数据库服务,这在单元测试中不是首选。此外,我在这里不使用 Mockito,而是使用 spock 和 groovy。
  • 您应该模拟数据库调用而不是私有方法。这是关于为什么不能使用 Spock + Groovy 模拟私有方法的一个很好的答案:*.com/questions/26464980/…
  • 这个答案是关于“为什么你可以访问私有方法”,而不是为什么你不能