2016-02-19 96 views
5

我想使用PowerMockito來模擬我測試的代碼中的java.net.URL類的創建。基本上,我想阻止發生真正的HTTP請求,而不是1)在請求發生時檢查數據,2)在模擬的響應中提供自己的測試數據。這就是我想:無法使用PowerMockito/Mockito模擬URL類

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class, MockedHttpConnection.class }) 
public class Test { 
    URL mockedURL = PowerMockito.mock(URL.class); 
    MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class); 

... 
    PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL); 
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection); 

... 
} 

,我想測試看起來像這樣的代碼:

URL wlInvokeUrl = new URL(wlInvokeUrlString); 
connection = (HttpURLConnection) wlInvokeUrl.openConnection(); 

在我的測試場景之前我嘲笑wlInvokeUrlString匹配「MyURLString」。我也嘗試過使用whenNew行的各種其他形式,試圖注入模擬。無論我嘗試什麼,它都不會攔截構造函數。我想要做的就是「捕捉」openConnection()的調用,並讓它返回我的模擬HTTP連接而不是真正的連接。

我在同一個腳本中嘲笑了這個之前的其他類,並且這些工作正常。要麼我需要第二雙眼睛(可能是真的),或者URL類有一些獨特的東西。我注意到,如果我使用「whenNew(URL.class).withAnyArguments()」並將「thenReturn」更改爲「thenAnswer」,我可以觸發它。唯一的問題是我從來沒有得到我的代碼的URL調用。我看到的是URL.class的三參數構造函數的調用,所有參數都爲空。這個類是否來自Java運行時並且由測試運行器引導?任何幫助深表感謝。

+0

你不需要Powermock來測試這個。只是模擬字符串url,例如使用基於文件的路徑(「file://my-test.properties」)。這將起作用,並且你可以傳遞你需要的數據 –

+0

@JérémieB你能詳細說明一下嗎?這看起來像是一個優雅而簡單的解決方案,但被測試的代碼需要HttpURLConnection,如果我將URL字符串更改爲引用文件(即java.lang.ClassCastException:sun.net.www.protocol。),我會得到一個ClassCastException。 ftp.FtpURLConnection不能轉換爲java.net.HttpURLConnection)。另外,如何使用這種方法驗證請求上的輸入參數? – Jim

+0

您不應該直接使用'HttpURLConnection',而是使用'URL'和'URLConnection'。 'HttpURLConnection'沒有什麼用處。這是URL抽象傳輸層的目的。 –

回答

0

我不確定.withParameterTypes(x).withArguments(x)之間的區別,但我相信您需要按照以下步驟對代碼進行設置。嘗試一下,讓我知道如果這沒有幫助。

PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection); 
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL); 
+0

我試過這些行,但是我看到了相同的結果。不使用模擬對象,而是創建並使用真正的URL和HttpURLConnection類。問題可能是因爲URL類被聲明爲final嗎?從我讀過的PowerMockito應該能夠處理這個問題。 – Jim

0

問題是真正的調用的參數與您的模擬中預期的不匹配。

PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL)將返回mockedURL只有呼叫是new URL("MyURLString")

如果將其更改爲:

PowerMockito.whenNew(URL.class).withParameterTypes(String.class) 
    .withArguments(org.mockito.Matchers.any(String.class)).thenReturn(mockedURL); 

它可以捉住傳遞給構造URL(String)(甚至null)任何字符串並返回你的模擬。


當你試圖

「whenNew(URL.class).withAnyArguments()」 和改變 「thenReturn」 到 「thenAnswer」 我能得到它的觸發。唯一的問題是我從來沒有得到我的代碼的URL調用 。我看到的是針對URL.class的 三參數構造函數的調用,其中 參數的所有空值。

PowerMock會盡量嘲笑所有構造函數(org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod在線路122),那麼它調用第一個(3個參數)和譏笑它的答案。但隨後的電話將返回已經嘲弄的電話,因爲您告訴它嘲笑任何參數。這就是爲什麼您在Answer中只看到null, null, null的一個電話。

+0

我在嘗試做這項工作的過程中嘗試了很多事情,包括對您的建議的變體。我通過更改whenNew這一行來重新測試,但仍得到相同的結果。任何其他想法? – Jim

0

這是使用PowerMockito.whenNew時常見的錯誤。

請注意,您必須準備創建用於測試的MyClass的新實例的類,而不是MyClass本身。例如。如果該類做新MyClass的()被稱爲X,那麼你不得不爲了做@PrepareForTest(X.class)爲whenNew工作

Powermock wiki

所以,你需要一點點改變您的測試,並加入到@PrepareForTest其創建的URL一個新實例類,如:

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class, MockedHttpConnection.class , ConnectionUser.class}) 
public class URLTest { 
public class URLTest { 

    private ConnectionUser connectionUser; 

    @Before 
    public void setUp() throws Exception { 

     connectionUser = new ConnectionUser(); 
    } 

    @Test 
    public void testName() throws Exception { 

     URL mockedURL = PowerMockito.mock(URL.class); 
     MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class); 

     PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL); 
     PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection); 

     connectionUser.open(); 

     assertEquals(mockedConnection, connectionUser.getConnection()); 


    } 
} 

其中:

public class ConnectionUser { 

    private String wlInvokeUrlString = "MyURLString"; 
    private HttpURLConnection connection; 

    public void open() throws IOException { 
     URL wlInvokeUrl = new URL(wlInvokeUrlString); 
     connection = (HttpURLConnection) wlInvokeUrl.openConnection(); 
    } 

    public HttpURLConnection getConnection() { 
     return connection; 
    } 
} 
+0

你的答案最接近工作。進行這些更改後,將調用模擬URL構造函數。但是,它總是返回一個空對象。通過這個,我的意思是'URL wlInvokeUrl = new URL(wlInvokeUrlString);'總是將wlInvokeUrl賦值爲null。我已經通過重構我的代碼解決了原始問題,但是如果您有任何其他建議,我仍然致力於看到PowerMockito爲此工作。 – Jim

+0

你使用什麼版本的PowerMock,Mockito,Java?我使用最新版本和Mockito 1.10.19進行了測試,只有在我替換'mockedUrl'via'null'時才能重現您的問題。像這樣:'PowerMockito.whenNew(URL.class).withArguments(「MyURLString」)。然後返回(null);'在所有其他情況下,我的版本的測試通過。順便說一下,我使用Java 8來運行測試。 –

+0

你可以嘗試在你的環境中運行我的版本嗎? –