Java 我在 Mockito 2.2 中使用什么代替 Whitebox 来设置字段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40280918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What do I use instead of Whitebox in Mockito 2.2 to set fields?
提问by emanciperingsivraren
When using Mockito 1.9.x I have been using Whitebox
to set values of fields to "inject" mocks. Se example below:
使用 Mockito 1.9.x 时,我一直在使用Whitebox
将字段值设置为“注入”模拟。下面的例子:
@Before
public void setUp() {
eventHandler = new ProcessEventHandler();
securityService = new SecurityServiceMock();
registrationService = mock(RegistrationService.class);
Whitebox.setInternalState(eventHandler, "registrationService", registrationService);
Whitebox.setInternalState(eventHandler, "securityService", securityService);
}
I really like this approach, but now that I tried to upgrade to Mockito
2.2.7
I noticed (or rather, my IDE noticed and told me quite a few times) that Whitebox was no longer to be found in Mockito.
我真的很喜欢这种方法,但是现在我尝试升级到Mockito
2.2.7
我注意到(或者更确切地说,我的 IDE 注意到并告诉我很多次)在 Mockito 中不再找到 Whitebox。
I have found one alternative, that can work as a replacement, and that is org.powermock.reflect.Whitebox
, the problem with that is that I get another dependency (Powermock), just to use Whitebox.
我找到了一个替代方案,它可以作为替代品,也就是说org.powermock.reflect.Whitebox
,问题在于我得到了另一个依赖项(Powermock),只是为了使用 Whitebox。
Powermock
also have a class named Whitebox
, but unfortunately it looks as if it can not be used with Mockito 2.2.x
Powermock
也有一个名为 的类Whitebox
,但不幸的是它看起来好像不能与Mockito 2.2.x
Is there any good alternatives in Mockito that I can use to manually "inject" fields, now that Whitebox
is no longer available?
Mockito 中是否有任何好的替代方案可以用来手动“注入”字段,现在Whitebox
不再可用?
Solution
解决方案
I wrote in a comment in response to the post made of @JeffBowman. In short I chose to copy the code of WhiteBox, and use that, since it is used in most of the test cases and the class does not have dependencies to other classes. It was the fastest path to solve this issue.
我在评论中写道,以回应@JeffBowman 的帖子。简而言之,我选择复制 WhiteBox 的代码并使用它,因为它用于大多数测试用例并且该类与其他类没有依赖关系。这是解决这个问题的最快途径。
NoteThe solution that @bcody suggest is a better alternative, if you are using spring, it ads no extra code for you to maintain. I got that information to late :(
注意@bcody 建议的解决方案是一个更好的选择,如果您使用的是 spring,它不会为您提供额外的代码来维护。我很晚才收到这些信息:(
采纳答案by Jeff Bowman
Note that Whitebox
was always in the org.mockito.internal
package. Beyond the incrementing of the major version number, the internal
designation is a giveaway that the package may be subject to breaking changes.
请注意,Whitebox
它始终在org.mockito.internal
包装中。除了主要版本号的增加之外,该internal
名称还表明软件包可能会受到重大更改。
If you do want to make it a point to set otherwise-inaccessible fields in your test, you can do so in the same way that setInternalState
does, which is just to identify the field in the hierarchy, call setAccessible
on it, and then set it. The full code is here on grepcode.You can also examine a number of other ways to set inaccessible state in tests.
如果您确实希望在测试中设置其他方式无法访问的字段,您可以按照与此相同的方式进行设置setInternalState
,即识别层次结构中的字段,调用setAccessible
它,然后设置它。完整代码在 grepcode 上。您还可以检查在测试中设置不可访问状态的许多其他方法。
public static void setInternalState(Object target, String field, Object value) {
Class<?> c = target.getClass();
try {
Field f = getFieldFromHierarchy(c, field); // Checks superclasses.
f.setAccessible(true);
f.set(target, value);
} catch (Exception e) {
throw new RuntimeException(
"Unable to set internal state on a private field. [...]", e);
}
}
However,in situations like this, my general advice is to stop fighting the tools: Java's four levels of encapsulation (public, protected, package, private) are not necessarily granular enough to express the degree of protection you're trying to express, and it's often much easier to add a well-documented initialization method or constructor override to override the dependencies as you're trying to do reflectively. If you put your tests in the same Java package as the class it tests, you can often even make the fields or method/constructor package-private, which is also a good reason to set up parallel source folders src
and tests
(etc) that represent two halves of the same Java package.
但是,在这种情况下,我的一般建议是停止与工具作斗争:Java 的四个封装级别(公共、受保护、包、私有)不一定足够细化来表达您试图表达的保护程度,并且当您尝试进行反思时,添加记录良好的初始化方法或构造函数覆盖来覆盖依赖项通常要容易得多。如果你把你的测试和它测试的类放在同一个 Java 包中,你通常甚至可以使字段或方法/构造函数包私有,这也是设置并行源文件夹src
和tests
(等)代表两个的一个很好的理由同一个 Java 包的一半。
Though some treat this additional method or constructor as "API pollution", I see it instead as coding to the requirements of one of your class's most important consumers—its own test. If you need a pristine external interface, you can easily define one separately such that you can hide any details you'd like. However, you may find you likethe ability to inject any real or mock implementation directly into your now-more-flexible component, at which point you may want to look into dependency injection patterns or frameworks.
尽管有些人将这种额外的方法或构造函数视为“API 污染”,但我认为它是针对您的类最重要的使用者之一的要求进行编码的——它自己的测试。如果您需要一个原始的外部接口,您可以轻松地单独定义一个,这样您就可以隐藏您想要的任何细节。但是,您可能会发现自己喜欢将任何真实的或模拟的实现直接注入到现在更加灵活的组件中的能力,此时您可能想要研究依赖注入模式或框架。
回答by bcody
If you are using Spring (the spring-test library specifically), you can simply use ReflectionTestUtils.setField
instead of Whitebox.setInternalState
如果您使用的是 Spring(特别是 spring-test 库),您可以简单地使用ReflectionTestUtils.setField
而不是Whitebox.setInternalState
回答by foo
The cleanest, neatest and most portableway without reinventing the wheel is to use Apache Commons' FieldUtils
.
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/FieldUtils.html
无需重新发明轮子的最干净、最整洁和最便携的方式是使用 Apache Commons 的FieldUtils
.
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/FieldUtils.html
The answer to your question would then be
那么你的问题的答案是
public static void setStaticFieldValue(
@NonNull final Class<?> clz,
@NonNull final String fieldName,
@NonNull final Object value) throws Exception {
final Field f = FieldUtils.getField(clz, fieldName, true);
FieldUtils.removeFinalModifier(f);
f.set(null, value);
}
回答by Murik
You can use FieldSetter in Mockito2.x
您可以在 Mockito2.x 中使用 FieldSetter
import org.mockito.internal.util.reflection.FieldSetter;
FieldSetter.setField(eventHandler,eventHandler.getClass().getDeclaredField("securityService"), securityService);
回答by Priyal85
Here with fest-reflectapi you can find an easy to use fluent API for reflection support. This is what I use as an alternative to Mockito's Whiltebox.
在这里使用fest-reflectapi,您可以找到一个易于使用的流畅 API 来支持反射。这是我用来替代 Mockito 的 Whiltebox 的。