2016-02-19 47 views
5

Sto cercando di utilizzare PowerMockito per deridere la creazione della classe java.net.URL nel mio codice che sto testando. Fondamentalmente, voglio evitare che si verifichi la vera richiesta HTTP e invece 1) controllare i dati quando viene effettuata la richiesta e 2) restituire i miei dati di test su una risposta derisoria. Questo è quello che sto cercando:Impossibile classificare la classe URL utilizzando PowerMockito/Mockito

@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); 

... 
} 

Il codice che voglio testare assomiglia a questo:

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

All'inizio della mia scenario di test ho deridere il wlInvokeUrlString per corrispondenza "MyURLString". Ho anche provato a utilizzare varie altre forme della linea whenNew, cercando di iniettare la simulazione. Non importa quello che provo, non intercetta mai il costruttore. Tutto quello che voglio fare è "intercettare" la chiamata a openConnection() e fare in modo che restituisca la mia connessione HTTP falsa invece di quella reale.

Ho deriso altre classi prima di questa nello stesso script e funzionano come previsto. O ho bisogno di un secondo paio di occhi (probabilmente vero) o c'è qualcosa di unico nella classe URL. Ho notato che se uso "whenNew (URL.class) .withAnyArguments()" e cambi "thenReturn" in "thenAnswer", potrei farlo attivare. L'unico problema è che non ottengo mai la chiamata dell'URL per il mio codice. Quello che vedo è un'invocazione del costruttore di 3 argomenti per URL.class con tutti i null per i parametri. Potrebbe essere questa classe provenire dal runtime Java e viene avviata dal test runner? Ogni aiuto è molto apprezzato.

+0

non hai bisogno di Powermock per testare questo. Basta prendere in giro l'URL della stringa, ad esempio per utilizzare un percorso basato su file ("file: //my-test.properties"). questo funzionerà, e puoi passare i dati che ti servono –

+0

@ JérémieB Puoi approfondire un po 'di più? Sembra una soluzione elegante e semplice, ma il codice sotto test si aspetta un HttpURLConnection e ottengo un ClassCastException se cambio la stringa URL per fare riferimento a un file (ad esempio, java.lang.ClassCastException: sun.net.www.protocol. ftp.FtpURLConnection non può essere lanciato su java.net.HttpURLConnection). Inoltre, come verificherai i parametri di input sulla richiesta utilizzando questo approccio? – Jim

+0

Non si dovrebbe usare direttamente 'HttpURLConnection', ma' URL' e 'URLConnection'. Non c'è nulla di utile in 'HttpURLConnection'. È lo scopo di un URL per astrarre il livello di trasporto. –

risposta

0

Non sono sicuro della differenza tra .withParameterTypes(x) e .withArguments(x) ma credo che sia necessario configurarlo come segue affinché il codice funzioni. Fare un tentativo e fammi sapere se questo non aiuta.

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

Ho provato queste righe, ma vedo gli stessi risultati. Invece di utilizzare gli oggetti derisi, vengono create e utilizzate le vere URL e le classi HttpURLConnection. Il problema potrebbe essere dovuto al fatto che la classe URL è dichiarata definitiva? Da quello che ho letto PowerMockito dovrebbe essere in grado di gestirlo. – Jim

0

Il problema è che gli argomenti della chiamata reale non corrispondono all'aspettativa prevista.

PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL) restituirà mockedURL solo la chiamata è new URL("MyURLString").

Se si cambia in:

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

Sarà catturare qualsiasi stringa passata al costruttore URL(String) (anche null) e restituire il finto.


quando si è tentato

"whenNew (URL.class) .withAnyArguments()" e cambiare il "thenReturn" per "thenAnswer" Potrei farlo scattare. L'unico problema è che non ottengo mai l' chiamata URL per il mio codice. Quello che vedo è un'invocazione del costruttore di argomenti 3 per URL.class con tutti i valori null per i parametri .

PowerMock cercherà di prendere in giro tutti costruttori (org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod alla linea 122), poi chiama il primo (con 3 argomenti) e prendere in giro la sua risposta. Ma le chiamate successive restituiranno il già deriso perché gli hai detto di prendere in giro qualsiasi argomento. Ecco perché vedi una sola chiamata con null, null, null nel tuo Answer.

+0

Ho provato molte cose nel tentativo di farlo funzionare, inclusa una variazione sul tuo suggerimento. Ho ripetuto il test modificando la linea whenNew come si cita, ma otteniamo comunque gli stessi risultati. Qualche altro pensiero? – Jim

0

È un errore comune quando si utilizza PowerMockito.whenNew.

Si noti che è necessario preparare la classe creando la nuova istanza di MyClass per il test, non la stessa MyClass. Per esempio. se la classe a fare nuovi MyClass() viene chiamato X, allora si avrebbe a che fare @PrepareForTest (X.class) in ordine per whenNew lavorare

Da Powermock wiki

Quindi, è necessario un cambiamento po ' il test e aggiungere ad @PrepareForTest una classe che creare una nuova istanza di URL piace:

@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()); 


    } 
} 

dove:

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

la tua risposta si avvicina di più al lavoro. Dopo aver apportato queste modifiche, viene richiamato il costruttore di falsi URL. Tuttavia, restituisce sempre un oggetto nullo. Con questo intendo la riga 'URL wlInvokeUrl = new URL (wlInvokeUrlString);' assegna sempre wlInvokeUrl a null. Ho risolto il problema originale risolvendo il mio codice, ma mi impegno ancora a vedere PowerMockito funzionare per questo nel caso abbiate altri suggerimenti. – Jim

+0

Quali versioni di PowerMock, Mockito, Java usi? Ho provato con l'ultima versione e Mockito 1.10.19 e posso riprodurre il tuo problema solo se sostituisco 'mockedUrl'via' null'. In questo modo: 'PowerMockito.whenNew (URL.class) .withArguments (" MyURLString "). ThenReturn (null);' In tutti gli altri casi viene passata la mia versione del test. A proposito, io uso Java 8 per eseguire test. –

+0

Provare a eseguire la mia versione nel proprio ambiente? –