問題描述
我想編寫一個 JUnit 測試來驗證下面的代碼是否使用了 BufferedInputStream:
I woud like to write a JUnit test to verify that the code below uses a BufferedInputStream:
public static final FilterFactory BZIP2_FACTORY = new FilterFactory() {
public InputStream makeFilter(InputStream in) {
// a lot of other code removed for clarity
BufferedInputStream buffer = new BufferedInputStream(in);
return new CBZip2InputStream(buffer);
}
};
(FilterFactory 是一個接口.)
(FilterFactory is an interface.)
到目前為止,我的測試如下所示:
My test thus far looks like this:
@Test
public void testBZIP2_FactoryUsesBufferedInputStream() throws Throwable {
InputStream in = mock(InputStream.class);
BufferedInputStream buffer = mock(BufferedInputStream.class);
CBZip2InputStream expected = mock(CBZip2InputStream.class);
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);
assertEquals(expected, observed);
}
對 PowerMockito.spy 的調用引發異常并顯示以下消息:
The call to PowerMockito.spy raises an exception with this message:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class edu.gvsu.cis.kurmasz.io.InputHelper$1
Mockito can only mock visible & non-final classes.
我應該使用什么來代替 PowerMocktio.spy 來設置對 whenNew 的調用?
What should I be using instead of PowerMocktio.spy to set up the calls to whenNew?
推薦答案
信息很明顯:你不能模擬非可見類和 final 類.簡短回答:為您的匿名類創建一個命名類,然后測試這個類!
The message is pretty obvious: You can't mock non-visible and final classes. Short answer : Create a named class of your anonymous one, and test this class instead!
長答案,讓我們來挖掘一下原因!
Long answer, let's dig why !
你實例化一個FilterFactory
的匿名類,當編譯器看到一個匿名類時,它會創建一個final和package visible類.所以匿名類不能通過標準方法模擬,即通過 Mockito.
You instantiate an anonymous class of FilterFactory
, when the compiler sees an anonymous class, it creates a final and package visible class. So the anonymous class is not mockable through standard mean i.e. through Mockito.
好的,現在假設您希望能夠通過 Powermock 模擬這個匿名類.當前編譯器使用以下方案編譯匿名類:
OK, now suppose you want to be able to mock this anonymous class through Powermock. Current compilers compile anonymous class with following scheme :
Declaring class + $ + <order of declaration starting with 1>
模擬匿名類可能但很脆弱(我是認真的)所以假設匿名類是第 11 個被聲明的類,它會顯示為
Mocking anonymous class possible but brittle (And I mean it) So supposing the anonymous class is the eleventh to be declared, it will appear as
InputHelper$11.class
所以你可以準備測試匿名類:
So you could potentially prepare for test the anonymous class:
@RunWith(PowerMockRunner.class)
@PrepareForTest({InputHelper$11.class})
public class InputHelperTest {
@Test
public void anonymous_class_mocking works() throws Throwable {
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
}
}
此代碼將編譯,但最終會在您的 IDE 中報告為錯誤.IDE 可能不知道 InputHelper$11.class
.不使用編譯類的IntelliJ檢查代碼報告如此.
This code will compile, BUT will eventually be reported as an error with your IDE. The IDE probably doesn't know about InputHelper$11.class
. IntelliJ who doesn't use compiled class to check the code report so.
匿名類命名實際上取決于聲明的順序這一事實也是一個問題,當有人之前添加另一個匿名類時,編號可能會改變.匿名類是為了保持匿名,如果編譯器決定有一天使用字母甚至隨機標識符怎么辦!
Also the fact that the anonymous class naming actually depends on the order of the declaration is a problem, when someone adds another anonymous class before, the numbering could change. Anonymous classes are made to stay anonymous, what if the compiler guys decide one day to use letters or even random identifiers!
所以通過 Powermock 模擬匿名類是可能的,但很脆弱,千萬不要在實際項目中這樣做!
編輯說明: Eclipse 編譯器有不同的編號方案,它總是使用 3 位數字:
EDITED NOTE : The Eclipse compiler has a different numbering scheme, it always uses a 3 digit number :
Declaring class + $ + <pad with 0> + <order of declaration starting with 1>
另外,我認為 JLS 并沒有明確規定編譯器應該如何命名匿名類.
Also I don't think the JLS clearly specify how the compilers should name anonymous classes.
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);
PowerMockito.spy
返回 spy,它不會改變 InputHelper.BZIP2_FACTORY
的值.所以你需要通過反射來實際設置這個字段.您可以使用 Powermock 提供的 Whitebox
實用程序.
PowerMockito.spy
returns the spy, it doesn't change the value of InputHelper.BZIP2_FACTORY
. So you would need to actually set via reflection this field. You can use the Whitebox
utility that Powermock provide.
只用模擬來測試匿名過濾器使用 BufferedInputStream
太麻煩了.
Too much trouble to just test with mocks that the anonymous filter uses a BufferedInputStream
.
我寧愿寫如下代碼:
一個將使用命名類的輸入助手,我不使用接口名稱向用戶說明此過濾器的意圖是什么!
An input helper that will use the named class, I don't use the interface name to make clear to the user what is the intent of this filter!
public class InputHelper {
public static final BufferedBZIP2FilterFactory BZIP2_FACTORY = new BufferedBZIP2FilterFactory();
}
現在是過濾器本身:
public class BufferedBZIP2FilterFactory {
public InputStream makeFilter(InputStream in) {
BufferedInputStream buffer = new BufferedInputStream(in);
return new CBZip2InputStream(buffer);
}
}
現在你可以編寫這樣的測試了:
Now you can write a test like this :
@RunWith(PowerMockRunner.class)
public class BufferedBZIP2FilterFactoryTest {
@Test
@PrepareForTest({BufferedBZIP2FilterFactory.class})
public void wraps_InputStream_in_BufferedInputStream() throws Exception {
whenNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class))
.thenReturn(Mockito.mock(CBZip2InputStream.class));
new BufferedBZIP2FilterFactory().makeFilter(anInputStream());
verifyNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class));
}
private ByteArrayInputStream anInputStream() {
return new ByteArrayInputStream(new byte[10]);
}
}
但如果你強制 CBZip2InputStream
只接受 BufferedInputStream
,最終可以避免這個測試場景的 powermock 東西.通常使用 Powermock 意味著設計有問題.在我看來,Powermock 非常適合遺留軟件,但在設計新代碼時會使開發人員失明;由于他們錯過了 OOP 的優點,我什至會說他們正在設計遺留代碼.
But could eventually avoid powermock stuff for this test scenario if you force the CBZip2InputStream
to only accept BufferedInputStream
. Usually using Powermock means something is wrong with the design. In my opinion Powermock is great for legacy softwares, but can blind developers when designing new code; as they are missing the point of OOP's good part, I would even say they are designing legacy code.
希望有幫助!
這篇關于在匿名類中測試方法時,如何使用 Powermockito 模擬新對象的構造?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!