我有一个类,我想使用一个调用私有方法的公共方法进行测试。 我想假设私有方法可以正常工作。 例如,我想要类似doReturn....when...的东西。 我发现使用PowerMock可能有解决方案,但是该解决方案对我不起作用。

怎么做? 有人有这个问题吗?

另一个选择是使私有方法受到保护,并在测试用例中为其添加替代。

通常,如果需要存根私有方法,则对象模型有问题-您是否考虑过重构?

@Emma为什么? 如果他的方法调用某些外部资源(例如db),并且想对其进行模拟以注入一些假结果怎么办?

@grinch他应该在单独的适配器类中提取用于访问外部资源的代码。 这样,他可以轻松模拟适配器类,并将测试的类中的(业务)逻辑与访问外部资源的技术细节分开。

我在这里没有问题。使用Mockito API的以下代码,我做到了:

public class CodeWithPrivateMethod {
public void meaningfulPublicApi() {
if (doTheGamble("Whatever", 1 << 3)) {
throw new RuntimeException("boom");
}
}
private boolean doTheGamble(String whatever, int binary) {
Random random = new Random(System.nanoTime());
boolean gamble = random.nextBoolean();
return gamble;
}
}

这是JUnit测试:

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 static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.support.membermodification.MemberMatcher.method;
@RunWith(PowerMockRunner.class)
@PrepareForTest(CodeWithPrivateMethod.class)
public class CodeWithPrivateMethodTest {
@Test(expected = RuntimeException.class)
public void when_gambling_is_true_then_always_explode() throws Exception {
CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod());
when(spy, method(CodeWithPrivateMethod.class,"doTheGamble", String.class, int.class))
.withArguments(anyString(), anyInt())
.thenReturn(true);
spy.meaningfulPublicApi();
}
}

感谢您提供此答案,他们Wiki上的示例代码仅显示了与EasyMock后端一起使用的API,而不是与Mockito一起使用的API。

@ Brice-Great,清晰直接的例子!!

请注意在其IDE内容中具有Hamcrest匹配器类的其他开发人员会帮助他们:不能使用Mockitos .withArguments()方法-您必须使用Mockito匹配器! ;)花了我一段时间来弄清楚为什么测试代码总是抛出异常。

@Brice但是,如何从" when(spy,method(.....)"中管理异常?拥有测试抛出异常或使用try catch很好吗?

RuntimeException和@Expected(...)只是示例的标准值。在JUnit的过去几年中,我发现try catch是目前测试异常行为的最佳方法,至少在Java 7上是如此(请参阅答案)。使用Java 8 lambda可以对此进行改进。

适用于任何测试框架(如果您的类不是final)的通用解决方案是手动创建自己的模拟。

将您的私有方法更改为保护。

在您的测试班级中扩展班级

覆盖以前的私有方法以返回所需的任何常量

它不使用任何框架,因此它不那么优雅,但是它将始终有效:即使没有PowerMock。或者,如果您已经执行了步骤1,则可以使用Mockito来执行步骤2和3。

要直接模拟私有方法,您需要使用PowerMock,如其他答案所示。

这真是个天才。。谢谢。

@ArtB如果将private方法更改为protected,则不再需要创建自己的模拟程序,因为在整个包中也可以使用protected。 (并且测试应该与要测试的类属于同一包)。

@AnthonyRaymond但如果该方法正在做您不想在测试中做的事情怎么办?例如,我有一些旧代码正在打开一个硬编码的本地文件,该文件应该在生产环境中存在,而在开发人员中不存在;如果在开发中完成,则会导致死亡。我使用这种技术来跳过该文件的读取。

@ArtB我同意,但是问题是"如何模拟私有方法",这意味着该类将成为模拟对象。因此,如果该方法是可见性程序包,则可以根据期望模拟该方法。国际海事组织扩大班级而不是嘲笑是用锤子砸苍蝇。

谢谢,我尝试了多种解决方案,这是如此简单,出色。让我感到羞耻,没有马上想到同样的事情。

由于某种原因,Brice的答案对我不起作用。我能够对其进行一些操作以使其正常工作。可能只是因为我拥有PowerMock的较新版本。我正在使用1.6.5。

import java.util.Random;
public class CodeWithPrivateMethod {
public void meaningfulPublicApi() {
if (doTheGamble("Whatever", 1 << 3)) {
throw new RuntimeException("boom");
}
}
private boolean doTheGamble(String whatever, int binary) {
Random random = new Random(System.nanoTime());
boolean gamble = random.nextBoolean();
return gamble;
}
}

测试类如下所示:

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 static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.doReturn;
@RunWith(PowerMockRunner.class)
@PrepareForTest(CodeWithPrivateMethod.class)
public class CodeWithPrivateMethodTest {
private CodeWithPrivateMethod classToTest;
@Test(expected = RuntimeException.class)
public void when_gambling_is_true_then_always_explode() throws Exception {
classToTest = PowerMockito.spy(classToTest);
doReturn(true).when(classToTest,"doTheGamble", anyString(), anyInt());
classToTest.meaningfulPublicApi();
}
}

听起来似乎很明显,但是我很难导入org.mockito.Mockito.doReturn而不是org.powermock.api.mockito.PowerMockito.doReturn。一旦我使用了正确的导入功能,@ gaoagong的上述代码对我来说就是一种享受

我知道一种可以调用您的私有函数以在Mockito中进行测试的方式

@Test
public  void  commandEndHandlerTest() throws  Exception
{
Method retryClientDetail_privateMethod =yourclass.class.getDeclaredMethod("Your_function_name",null);
retryClientDetail_privateMethod.setAccessible(true);
retryClientDetail_privateMethod.invoke(yourclass.class, null);
}

不带参数:

ourObject = PowerMockito.spy(new OurClass());
when(ourObject ,"ourPrivateMethodName").thenReturn("mocked result");

使用String参数:

ourObject = PowerMockito.spy(new OurClass());
when(ourObject, method(OurClass.class,"ourPrivateMethodName", String.class))
.withArguments(anyString()).thenReturn("mocked result");