問題描述
在使用 Mockito 1.9.x 時,我一直在使用 Whitebox
將字段值設置為注入"模擬.請參閱下面的示例:
@Before公共無效設置(){eventHandler = new ProcessEventHandler();securityService = new SecurityServiceMock();注冊服務 = 模擬(注冊服務.class);Whitebox.setInternalState(eventHandler, "registrationService", registrationService);Whitebox.setInternalState(eventHandler, "securityService", securityService);}
我真的很喜歡這種方法,但現(xiàn)在我嘗試升級到 Mockito
2.2.7
我注意到(或者更確切地說,我的 IDE 注意到并告訴了我很多次),在 Mockito 中不再可以找到 Whitebox.
我找到了一種替代方案,可以作為替代品,那就是 org.powermock.reflect.Whitebox
,問題是我得到了另一個依賴項(Powermock),只是為了使用 Whitebox.
Powermock
也有一個名為 Whitebox
的類,但不幸的是它看起來好像不能與 Mockito 2.2.x
一起使用p>
既然 Whitebox
不再可用,我可以使用 Mockito 中的任何好的替代方法來手動注入"字段嗎?
解決方案
我在評論中回復@JeffBowman 的帖子.簡而言之,我選擇復制 WhiteBox 的代碼并使用它,因為它在大多數(shù)測試用例中使用,并且該類與其他類沒有依賴關系.這是解決此問題的最快途徑.
注意 @bcody 建議的解決方案是一個更好的選擇,如果您使用的是 spring,它不需要額外的代碼供您維護.我很晚才得到這些信息:(
請注意,Whitebox
始終位于 org.mockito.internal
包中.除了主要版本號的遞增之外,internal
名稱表明該軟件包可能會受到重大更改.
如果您確實想在測試中設置其他無法訪問的字段,您可以像 setInternalState
一樣進行設置,這只是在層次結構,在其上調用 setAccessible
,然后設置它.完整代碼在 grepcode 上. 您還可以查看許多 在測試中設置不可訪問狀態(tài)的其他方法.
public static void setInternalState(Object target, String field, Object value) {類<?>c = 目標.getClass();嘗試 {字段 f = getFieldFromHierarchy(c, field);//檢查超類.f.setAccessible(true);f.set(目標,值);} 捕捉(異常 e){拋出新的運行時異常(無法在私有字段上設置內部狀態(tài).[...]",e);}}
然而,在這種情況下,我的一般建議是停止使用工具:Java 的四個封裝級別(公共、受保護、包、私有)不是必須足夠細化以表達您嘗試表達的保護程度,并且通常更容易添加一個記錄良好的初始化方法或構造函數(shù)覆蓋來覆蓋依賴關系,因為您正在嘗試進行反思.如果您將測試與其測試的類放在同一個 Java 包中,您通常甚至可以將字段或方法/構造函數(shù)設置為包私有,這也是設置并行源文件夾 src
和 tests
(etc) 代表同一個 Java 包的兩半.
雖然有些人將這種額外的方法或構造函數(shù)視為API 污染",但我認為它是針對您的類中最重要的消費者之一(它自己的測試)的要求進行的編碼.如果您需要一個原始的外部接口,您可以輕松地單獨定義一個,這樣您就可以隱藏您想要的任何細節(jié).但是,您可能會發(fā)現(xiàn)您喜歡將任何真實或模擬實現(xiàn)直接注入到您現(xiàn)在更靈活的組件中的能力,此時您可能需要研究依賴注入模式或框架.p>
When using Mockito 1.9.x I have been using Whitebox
to set values of fields to "inject" mocks. Se example below:
@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.
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.
Powermock
also have a class named Whitebox
, but unfortunately it looks as if it can not be used with 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?
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.
Note The 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 :(
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.
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.
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.
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 like the 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.
這篇關于我在 Mockito 2.2 中使用什么代替 Whitebox 來設置字段?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!