2010-02-13 11 views
13

Qualcuno può suggerire come utilizzare al meglio EasyMock per aspettarsi una chiamata a Runtime.getRuntime().exec(xxx)?Mock Runtime.getRuntime()?

Potrei spostare la chiamata in un metodo in un'altra classe che implementa un'interfaccia, ma preferirei non in un mondo ideale.

interface RuntimeWrapper { 
    ProcessWrapper execute(String command) throws IOException; 
} 

interface ProcessWrapper { 
    int waitFor() throws InterruptedException; 
} 

Mi chiedevo se qualcuno avesse altri suggerimenti?

risposta

13

La classe non deve chiamare Runtime.getRuntime(). dovrebbe aspettarsi che Runtime sia impostato come dipendenza e che lavori con esso. Quindi nel tuo test puoi facilmente fornire una simulazione e impostarla come dipendenza.

Come sidenote, suggerirei di guardare this lecture on OO Design for testability.

Aggiornamento: Non ho visto il costruttore privato. Puoi provare a utilizzare java bytecode instrumentation per aggiungere un altro costruttore o rendere pubblico il costruttore, ma ciò potrebbe rivelarsi impossibile (se ci sono alcune restrizioni su quella classe).

Quindi la tua opzione è quella di creare un wrapper (come suggerito nella domanda), e seguire l'approccio di iniezione delle dipendenze.

+1

Grazie per il suggerimento - Sono d'accordo che l'iniezione la dipendenza è il modo migliore, ma io preferirei prendere in giro esso. Tuttavia, non riesco a vedere un modo per ottenere un'istanza derisa di Runtime - non è un'interfaccia e non sono sicuro di poter creare una sottoclasse perché ha un costruttore privato. Forse mi manca qualcosa? – Rich

+0

sì, che rende quasi impossibile. Controlla il mio aggiornamento. – Bozho

+0

Ho intenzione di andare con l'approccio wrapper :) Grazie ancora! – Rich

0

Forse invece di prendere in giro Runtime.getRuntime().exec() si potrebbe "prendere in giro" lo script/programma/ecc. dovrebbe chiamare.

Invece di passare la stringa della riga di comando reale in exec(), scrivere uno script di prova ed eseguirlo. Si potrebbe fare in modo che lo script restituisca valori codificati che è possibile testare proprio come una classe derisa.

+0

Questo è quello che ho provato all'inizio, ma ho trovato alcuni problemi con questo. Innanzitutto rompe l'indipendenza della piattaforma dai test (anche se il codice è progettato per Windows, i test sono spesso eseguiti su una macchina Linux) e in secondo luogo, per qualche ragione mi spaventa prendermi gioco della sceneggiatura. Probabilmente perché ho paura di controllarlo :) Inoltre, il runtime di simulazione mi consente di simulare più facilmente scenari diversi. Grazie comunque! – Rich

6

Bozho sopra è IMO il Soluzione corretta. Ma non è l'unica soluzione. È possibile utilizzare PowerMock o JMockIt.

Utilizzando PowerMock:

package playtest; 

public class UsesRuntime { 
    public void run() throws Exception { 
     Runtime rt = Runtime.getRuntime(); 
     rt.exec("notepad"); 
    } 
} 


package playtest; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.legacy.PowerMockRunner; 

import static org.powermock.api.easymock.PowerMock.*; 
import static org.easymock.EasyMock.expect; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ UsesRuntime.class }) 
public class TestUsesRuntime { 

    @Test 
    public void test() throws Exception { 
     mockStatic(Runtime.class); 
     Runtime mockedRuntime = createMock(Runtime.class); 

     expect(Runtime.getRuntime()).andReturn(mockedRuntime); 

     expect(mockedRuntime.exec("notepad")).andReturn(null); 

     replay(Runtime.class, mockedRuntime); 

     UsesRuntime sut = new UsesRuntime(); 
     sut.run(); 
    } 
} 
+0

Grazie per il suggerimento, non avevo mai sentito parlare di Powermock. – Rich

0

Ecco come si dovrebbe farlo con EasyMock 3.0 (e JUnit 4):

import org.junit.*; 
import org.easymock.*; 
import static org.easymock.EasyMock.*; 

public final class EasyMockTest extends EasyMockSupport 
{ 
    @Test 
    public void mockRuntimeExec() throws Exception 
    { 
     Runtime r = createNiceMock(Runtime.class); 

     expect(r.exec("command")).andReturn(null); 
     replayAll(); 

     // In tested code: 
     r.exec("command"); 

     verifyAll(); 
    } 
} 

L'unico problema con la prova di cui sopra è che i bisogni di oggetto Runtime da passare al codice sotto test, che impedisce di utilizzare Runtime.getRuntime(). Con JMockit, d'altra parte, il seguente test si può scrivere, evitando il problema:

import org.junit.*; 
import mockit.*; 

public final class JMockitTest 
{ 
    @Test 
    public void mockRuntimeExec() throws Exception 
    { 
     final Runtime r = Runtime.getRuntime(); 

     new NonStrictExpectations(r) {{ r.exec("command"); times = 1; }}; 

     // In tested code: 
     Runtime.getRuntime().exec("command"); 
    } 
}