2010-02-13 38 views
13

¿Alguien puede hacer alguna sugerencia sobre la mejor manera de usar EasyMock para esperar una llamada al Runtime.getRuntime().exec(xxx)?Mock Runtime.getRuntime()?

Podría mover la llamada a un método en otra clase que implemente una interfaz, pero preferiría no hacerlo en un mundo ideal.

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

interface ProcessWrapper { 
    int waitFor() throws InterruptedException; 
} 

Me preguntaba si alguien tenía alguna otra sugerencia?

Respuesta

13

Su clase no debe llamar al Runtime.getRuntime(). debería esperar que se establezca un Runtime como su dependencia, y trabaje con él. Luego, en su prueba, puede proporcionar fácilmente un simulacro y establecerlo como una dependencia.

Como nota al margen, sugiero ver this lecture on OO Design for testability.

Actualización: No vi el constructor privado. Puedes intentar usar java bytecode instrumentation para agregar otro constructor o hacer que el constructor sea público, pero también puede ser imposible (si hay algunas restricciones en esa clase).

Así que su opción es crear un contenedor (como sugirió en la pregunta) y seguir el enfoque de inyección de dependencia.

+1

Gracias por la sugerencia - Estoy de acuerdo que la inyección de la dependencia es la mejor manera, pero yo preferiría para burlarse de él. Sin embargo, no puedo ver una forma de obtener una instancia de Runtime que se burló: no es una interfaz y no estoy seguro de poder subclasificarla porque tiene un constructor privado. Tal vez me estoy perdiendo algo? – Rich

+0

yup, eso lo hace casi imposible. Revisa mi actualización – Bozho

+0

Voy a ir con el enfoque envoltorio :) Gracias de nuevo! – Rich

0

Quizás en lugar de burlarse de Runtime.getRuntime().exec() podría "burlarse" de la secuencia de comandos/programa/etc. se supone que está llamando.

En lugar de pasar la cadena de línea de comandos real a exec(), escriba un script de prueba y ejecútelo. Podría hacer que la secuencia de comandos devuelva valores codificados con los que podría comparar al igual que una clase burlada.

+0

Eso es lo que intenté al principio, pero encontré algunos problemas con eso. En primer lugar, rompe la independencia de la plataforma de las pruebas (a pesar de que el código está diseñado para Windows, las pruebas a menudo se ejecutan en una caja Linux) y en segundo lugar, por alguna razón, me asusta burlar el guión. Probablemente porque tengo miedo de registrarlo :) Además, el tiempo de ejecución burlón me permite simular diferentes escenarios más fácilmente. ¡Gracias de cualquier manera! – Rich

6

Bozho anterior es IMO Solución correcta. Pero no es la única solución. Puede usar PowerMock o JMockIt.

Usando 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

Gracias por la sugerencia, no había oído hablar de Powermock antes. – Rich

0

Aquí es cómo usted lo haría con EasyMock 3.0 (y 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(); 
    } 
} 

El único problema con la prueba anterior es que el Runtime necesidades de objetos para pasar al código bajo prueba, lo que le impide usar Runtime.getRuntime(). Con JMockit, por el contrario, el siguiente ensayo se puede escribir, evitando de ese 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"); 
    } 
} 
Cuestiones relacionadas