如何使用PowerMockito模拟私有静态方法?

4

这与此处发现的问题相同。不幸的是,被接受的答案对我没有用。我有一个静态实用程序类,其中包含我需要测试的私有方法。我发现当我像这样模拟方法时:

PowerMockito.spy(StaticUtil.class);
PowerMockito.when(StaticUtil.class, "getSomethingMethod", someObjectArray, someStringArray, aBoolean, someList).thenReturn(anotherList);

我遇到了空指针异常,因为实际上正在调用getSomethingMethod()。当我进行调试时,我发现在运行我要测试的方法时它没有被调用,但是在设置模拟对象时却被调用了。根据这个网站,当你以这种格式创建模拟对象时,这就是应该发生的事情。
然后我尝试以这种方式设置模拟对象:
PowerMockito.spy(StaticUtil.class);        
PowerMockito.doReturn(anotherList).when(StaticUtil.getSomethingMethod( someObjectArray, someStringArray, aBoolean, someList);

然而,Eclipse报错提示我需要将getSomethingMethod()的可见性改为public。使用PowerMockito的一个重要优势不就是可以mock私有方法吗?我需要mock这个private static方法(在设置期间不实际调用该方法)。

你能贴出更多测试代码吗?你的测试类是否已经准备好进行PowerMocking了? - Karol Lewandowski
猜测一下,你没有@PrepareForTest - user180100
2个回答

8

您需要使用在您所链接的答案中指定的确切语法。这个语法是doReturn(returnValue).when(Class, String, arguments);。您提供的两个示例都没有使用该示例。


这里有一些扩展说明。我已经创建了一个简单的测试框架来演示此操作:

尝试在这个类上运行测试:

package org.test.stackoverflow;

import java.util.Collections;
import java.util.List;

public class StaticUtil {
  public static void Wrapper() {
    getSomethingMethod(null, null, false, Collections.<String>emptyList());
  }

  private static List<String> getSomethingMethod(Object[] obj,
      String[] str, boolean flag, List<String> aList){ 
    System.out.println("I happen!");
    return aList;
  }
}

如果该方法被调用,我们将会看到I happen!的输出。如果没有被调用,我们则看不到。

接下来,我使用这个测试类:

package org.test.stackoverflow;

import java.util.List;

import org.junit.runner.RunWith;
import org.junit.*;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(org.test.stackoverflow.StaticUtil.class)
public class StaticUtilTest {
  Object[] someObjectArray;
  String[] someStringArray;
  boolean aBoolean;
  List<String> someList;
  List<String> anotherList;

  @Test
  public void testWhenClassStringMethod() throws Exception {
    System.out.println("Beginning Test when(Class klass, String method name).doReturn(result)");
    PowerMockito.spy(StaticUtil.class);
    PowerMockito.when(StaticUtil.class, "getSomethingMethod", someObjectArray, someStringArray, aBoolean, someList).thenReturn(anotherList);
    System.out.println("End Test when");
  }

  @Test
  public void testDoReturnActualMethod() throws Exception {
    PowerMockito.spy(StaticUtil.class);
    // This doesn't compile as you've correctly stated
//    PowerMockito.doReturn(anotherList).when(StaticUtil.getSomethingMethod(someObjectArray, someStringArray, aBoolean, someList);
  }

  @Test
  public void testDoReturnClassStringMethod() throws Exception {
    System.out.println("Beginning Test doReturn().when(Class klass, String method name");
    PowerMockito.spy(StaticUtil.class);
    PowerMockito.doReturn(anotherList).when(StaticUtil.class, "getSomethingMethod", someObjectArray, someStringArray, aBoolean, someList);
    System.out.println("End Test doReturn");
  }
}

所以,如果它输出I happen,那么我们使用了错误的语法。运行此程序时,我们会得到:

Beginning Test when(Class klass, String method name).doReturn(result)
I happen!
End Test when
Beginning Test doReturn().when(Class klass, String method name)
End Test doReturn

因此,您必须使用第三个测试中的语法
注意:此示例使用静态的、空的参数;显然,您应该根据您的应用程序适当地配置示例以使用Argument Matchers

非常感谢您提供详细的答案,我在这上面花费了很长时间!第三个测试中的语法完美地运行了。非常感谢您!谢谢!谢谢! - user3897392
第三个测试是如何工作的?“我发生了!”字符串没有被打印出来,并不意味着模拟已经正确配置。在这里,您尝试模拟StaticUtil类,但您没有调用测试方法包装器。请参见我的另一个答案。 - kswaughs

-3

当您设置模拟对象的期望时,必须使用参数匹配器(如Matchers.any()或Matchers.anyString()),而不是实际参数。

有关更多详细信息,请参见我的答案J-Unit Test: Make static void method in final class throw exception

durron597's answer存在潜在问题:'testDoReturnClassStringMethod'中的语法未正确模拟。 在该方法中,他尝试模拟StaticUtil类,但没有调用测试方法包装器。请参考以下示例

@Test
public void testDoReturnClassStringMethod() throws Exception {
    System.out.println("Beginning Test doReturn().when(Class klass, String method name");
    PowerMockito.spy(StaticUtil.class);
    PowerMockito.doReturn(anotherList).when(StaticUtil.class, "getSomethingMethod", someObjectArray, someStringArray, aBoolean, someList);
    StaticUtil.Wrapper();
    System.out.println("End Test doReturn");
}

这个的结果是

Beginning Test doReturn().when(Class klass, String method name
I happen!
End Test doReturn

打印出"I happen!"。模拟未正确配置。

正确的模拟方式是:

@Test
public void testDoReturnWithProperMock() throws Exception {
    System.out.println("Beginning Test doReturn().when(Class klass, String method name");
    PowerMockito.spy(StaticUtil.class);
    PowerMockito.doReturn(anotherList).when(StaticUtil.class, "getSomethingMethod", Matchers.anyObject(), Matchers.anyObject(), Matchers.anyBoolean(), Matchers.anyList());
    StaticUtil.Wrapper();
    System.out.println("End Test doReturn");

}

针对此,结果是:

Beginning Test doReturn().when(Class klass, String method name
End Test doReturn

请给出降低投票的原因,以更新正确答案。 - kswaughs
3
这是一个边缘链接回答。您应该扩展您的回答,包括尽可能多的信息,并仅将链接用于参考。 - Kyll
1
这是一个仅包含链接的回答,指向@kswaughs回答的另一个SO问题。要么将相关信息移植到此处,要么将此问题标记为链接问题的副本。 - dcsohl
3
您最先发布了这个回答,但后来又发布了第二个回答。您本可以编辑这个(第一个)答案并添加额外的信息。请在您的答案下方查看“编辑”链接。而且,您提供的链接只是 Stack Overflow 上的另一个答案,如果它已经回答了这个问题,那么这可能是一个重复的问题?请参阅此问题:我们如何处理仅为其他 Stack Overflow 答案链接的答案? :) - James

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