如何模拟通过PowerMock返回void的静态方法?


70

我的项目中有一些静态util方法,其中一些只是传递或引发异常。关于如何模拟具有除void以外的返回类型的静态方法,有很多示例。但是,我该如何模拟将void返回为“ doNothing()”的静态方法?

非无效版本使用以下代码行:

@PrepareForTest(StaticResource.class)

...

PowerMockito.mockStatic(StaticResource.class);

...

Mockito.when(StaticResource.getResource("string")).thenReturn("string");

但是,如果将其应用于StaticResourcesreturn void,则编译将抱怨when(T)不适用于void ...

有任何想法吗?

解决方法可能是让所有静态方法都返回一些Boolean成功方法,但我不喜欢这种方法。


是PowerMockito还是PowerMock的问题?标题建议使用PowerMock,但看起来像您在使用PowerMockito。
xpioneer

Answers:


39

您可以像在真实实例上使用Mockito一样进行操作。例如,您可以链接存根,以下行将使第一个调用不执行任何操作,然后第二个和以后的调用getResources将引发异常:

// the stub of the static method
doNothing().doThrow(Exception.class).when(StaticResource.class);
StaticResource.getResource("string");

// the use of the mocked static code
StaticResource.getResource("string"); // do nothing
StaticResource.getResource("string"); // throw Exception

感谢Matt Lachman的评论,请注意,如果在模拟创建时未更改默认答案,则默认情况下,模拟将不执行任何操作。因此,编写以下代码等效于不编写代码。

doNothing().doThrow(Exception.class).when(StaticResource.class);
StaticResource.getResource("string");

尽管这样说,但是对于阅读该测试的同事来说可能会很有趣,因为您对于此特定代码没有任何期望。当然,可以根据对测试的可理解性如何进行调整。


顺便说一下,以我的拙见,如果您要编写新代码,则应避免模拟静态代码。在Mockito,我们认为这通常是不良设计的提示,它可能导致可维护性差的代码。尽管现有的遗留代码是另外一回事了。

一般来说,如果您需要模拟私有方法或静态方法,则此方法会执行过多操作,应将其外部化到将注入到测试对象中的对象中。

希望能有所帮助。

问候


2
不幸的是,当when()仅接受一个变量,而StaticResource是一种类型时,它将无法正常工作。(StaticResource cannot be resolved to a variable
皮特

哦,对不起,您是正确的,我的代码是错误的,我习惯于使用非静态模拟。无论如何,我更新了答案以反映正确的语法。
布莱斯2012年

谢谢!因此,拥有没有依赖关系的静态辅助方法是一个坏主意吗?当然,我可以只注射和对象做的工作,但它的感觉就像是有意义的把那些在一个静态对象不依赖工人表示他们的独立性..
皮特

我的意思是,如果需要模拟您的静态辅助方法,那么您可能会认真考虑注入一个协作者。相反,您无需模拟StringUtils,Iterables或其他任何东西。同样不要忘记我们使用的是面向对象的语言,这全都与对象之间的消息传递有关(引用Alan Kay)。如果您有兴趣,那么有一本很棒的书:《由Tests指导的日益增长的面向对象软件》。问候:)
布莱斯

主导doNothing()呼叫是多余的。您只需要doThrow(new Exception()).when(StaticResource.class);在那条线上。
马特·拉赫曼

81

您可以像下面这样对静态void方法进行存根

PowerMockito.doNothing().when(StaticResource.class, "getResource", anyString());

尽管我不确定为什么要打扰,因为默认情况下,当调用mockStatic(StaticResource.class)时,StaticResource中的所有静态方法都已存根

更有用的是,您可以像这样捕获传递给StaticResource.getResource()的值:

ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
PowerMockito.doNothing().when(
               StaticResource.class, "getResource", captor.capture());

然后,您可以像下面这样评估传递给StaticResource.getResource的字符串:

String resourceName = captor.getValue();

1
能够做到这一点非常酷,因为它使您可以模拟Thread.sleep(long millis)来进行不同时间/更少时间/根本没有时间的睡眠!
fragorl 2014年

有没有一种方法可以避免在方法中使用字符串-在您的示例中为“ getResource”?
卡斯珀·图勒·汉森

@CasperThuleHansen,您可以潜在地使用反射并获取方法名称
user1697575

上述方法的最后一部分是参数,可以根据需要使用多个参数,并用逗号分隔。
Smart Coder

14

用更简单的术语来说,想像一下是否要在以下行进行模拟:

StaticClass.method();

然后在下面的代码行中进行模拟:

PowerMockito.mockStatic(StaticClass.class);
PowerMockito.doNothing().when(StaticClass.class);
StaticClass.method();

1
它可以用于StaticClass.class的多种方法,例如StaticClass.method();吗?StaticClass.method1(); StaticClass.method2();
S Kumar

5

模拟一个静态方法,例如返回void Fileutils.forceMKdir(File file),

样例代码:

File file =PowerMockito.mock(File.class);
PowerMockito.doNothing().when(FileUtils.class,"forceMkdir",file);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.