如何模拟第三方代码中的受保护方法

3
使用Mockito,我想模拟一个类的属性,以便我可以验证输出。
public class MyClass extends ThirdPartyFramework {
  Output goesHere;

  @Override
  protected setup(){
    goesHere = new Output();
  }

  //...      
}

public abstract class ThirdPartyFramework {
  protected setup(){...}
  //...
}

我需要注入 Output 类的模拟对象,以验证我的代码是否写入了正确的输出。
但是我不能只使用 @InjectMock,因为 setup() 方法是在运行时中间调用的,并会覆盖我的注入。
而且我不能只是将 MyClass 中的 setup 公开,因为我正在使用的测试代码是通用的,需要对所有 ThirdPartyFramework 的子类起作用,因此我只有对 ThirdPartyFramework 的引用,这意味着 setup() 是受保护的。
3个回答

1
你是否决定使用Mockito?我问这个问题是因为Mockito FAQMockito常见问题解答中指出它不支持模拟静态方法,而在这种情况下,你需要设置方法来创建你的模拟而不是真正的输出。
我曾经在类似的情况下使用过PowerMock:
whenNew(NewInstanceClass.class).withArguments(any()).thenReturn(mockObject);

这意味着每次创建NewInstanceClass时,无论使用了哪些构造函数参数以及谁在什么时间构造了NewInstanceClass,我的mockObject都会被返回。

在PowerMock文档中,我还发现了以下示例:

PowerMock.expectNew(NewInstanceClass.class).andReturn(mockObject)

实际上,即使你受限于Mockito,PowerMock包含了与Mockito相关的帮助文档,可以用来解决这个问题,让你在所有测试中使用Mockito,并且用PowerMock来模拟构造对象。像这样:

whenNew(Output.class).withNoArguments().thenReturn(yourOutputMock);

这很不错,我可能得这么做,添加PowerMock的依赖关系可能是一项工作,但覆盖new操作符的能力将使这个问题变得微不足道。 - David Parks

1

我最终通过包装ThirdPartyFramework并将该类放置在与ThirdPartyFramework类相同的包中来解决此问题。

这样,我就可以使用Mockito模拟受保护的方法。然后,我能够使用@InjectMock注入Output对象的一个模拟,并通过该模拟控制其方法调用。


1
+1 我正准备自己回答这样的问题。始终要使用一些你可以按照需要进行模拟的东西来包装第三方库。 - Dawood ibn Kareem

0
怎么样添加一个“goesHere”的setter,然后在setup()中检查并且只有在它为空时才改变它的值。这样你就可以在测试中注入一个不会被覆盖的值。像这样:
protected void setGoesHere( Output output ){
    goesHere = output;
} 

@Override
protected void setup(){
    if(goesHere != null) goesHere = new Output();
}

希望这能有所帮助。

那个方法可以行得通,但不幸的是我正在测试框架上工作,所以我不能直接控制 MyClass,我无法通过重构 MyClass 来解决这个问题。 - David Parks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接