2011-06-29 17 views
22

Necesito ayuda con esto:métodos imitar de ámbito local con objetos Mockito

Ejemplo:

void method1{ 
    MyObject obj1=new MyObject(); 
    obj1.method1(); 
} 

Quiero burlarse obj1.method1() en mi prueba, pero para ser transparente, de modo que no quiero hacer y cambio de código ¿Hay alguna manera de hacer esto en Mockito?

Respuesta

13

Si realmente quiere evitar tocar este código, puede usar Powermockito (PowerMock para Mockito).

Con esto, entre muchas otras cosas, puede mock the construction of new objects de una manera muy fácil.

+0

Tenga en cuenta, pero para uso futuro utilizaré PowerMock – Xoke

+0

@edutesoy ¿Puede escribir el código completo? No puedo entender tu idea. No veo la relación entre la burla del constructor y la burla de la variabli local. – gstackoverflow

+1

Problema con este enfoque si uso EclEmma para la cobertura del código es que EclEmma da una cobertura de código del 0% para MyClass si agrego MyClass.class en @PrepareForTest en lugar de la cobertura del código real. Creo que esto es una limitación o error con la herramienta EclEmma. Alguna idea para superar este problema? –

9

De ninguna manera. Necesitará alguna inyección de dependencia, es decir, en lugar de tener el obj1 instanciado, debería ser proporcionado por alguna fábrica.

MyObjectFactory factory; 

public void setMyObjectFactory(MyObjectFactory factory) 
{ 
    this.factory = factory; 
} 

void method1() 
{ 
    MyObject obj1 = factory.get(); 
    obj1.method(); 
} 

A continuación, la prueba se vería así:

@Test 
public void testMethod1() throws Exception 
{ 
    MyObjectFactory factory = Mockito.mock(MyObjectFactory.class); 
    MyObject obj1 = Mockito.mock(MyObject.class); 
    Mockito.when(factory.get()).thenReturn(obj1); 

    // mock the method() 
    Mockito.when(obj1.method()).thenReturn(Boolean.FALSE); 

    SomeObject someObject = new SomeObject(); 
    someObject.setMyObjectFactory(factory); 
    someObject.method1(); 

    // do some assertions 
} 
+2

Esto es lo que tengo en mente, pero esta es la adición de un código más innecesaria porque necesito para burlarse de un método del objeto de alcance local. – Xoke

+0

¿Qué parte del código es innecesaria? –

+2

No quiero crear Factory para crear nuevas instancias de myObject y ese código es innecesario porque solo es necesario para simular el método de la variable de ámbito local. – Xoke

1

Usted puede hacer esto mediante la creación de un método de fábrica en MiObjeto:

class MyObject { 
    public static MyObject create() { 
     return new MyObject(); 
    } 
} 

luego se burlan de que con PowerMock.

Sin embargo, al burlarse de los métodos de un objeto de ámbito local, depende de que parte de la implementación del método permanezca igual. Entonces pierde la capacidad de refactorizar esa parte del método sin romper la prueba. Además, si anula los valores de retorno en el simulacro, puede pasar la prueba unitaria, pero el método puede comportarse inesperadamente al usar el objeto real.

En resumen, probablemente no deberías tratar de hacer esto. Más bien, dejando que la prueba de conducción de su código (también conocido como TDD), se llegaría a una solución como:

void method1(MyObject obj1) { 
    obj1.method1(); 
} 

que pasa en la dependencia, que se puede burlar fácilmente la prueba de la unidad.

1

El mejor enfoque es no tocar el código y burlarse del constructor, como en este ejemplo para burlarse de la creación de un objeto File dentro de un método. No olvide para poner la clase que creará el archivo en @PrepareForTest.

package hello.easymock.constructor; 

import java.io.File; 

import org.easymock.EasyMock; 
import org.junit.Assert; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.powermock.api.easymock.PowerMock; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest({File.class}) 
public class ConstructorExampleTest { 

    @Test 
    public void testMockFile() throws Exception { 

     // first, create a mock for File 
     final File fileMock = EasyMock.createMock(File.class); 
     EasyMock.expect(fileMock.getAbsolutePath()).andReturn("/my/fake/file/path"); 
     EasyMock.replay(fileMock); 

     // then return the mocked object if the constructor is invoked 
     Class<?>[] parameterTypes = new Class[] { String.class }; 
     PowerMock.expectNew(File.class, parameterTypes , EasyMock.isA(String.class)).andReturn(fileMock); 
     PowerMock.replay(File.class); 

     // try constructing a real File and check if the mock kicked in 
     final String mockedFilePath = new File("/real/path/for/file").getAbsolutePath(); 
     Assert.assertEquals("/my/fake/file/path", mockedFilePath); 
    } 

} 
24

La respuesta de puntos @edutesoy a la documentación de PowerMockito y menciones de burla constructor como una sugerencia, pero no menciona cómo aplicar eso a la problemática actual de la cuestión.

Aquí hay una solución basada en eso.Tomando el código de la pregunta: ¿

public class MyClass { 
    void method1{ 
     MyObject obj1=new MyObject(); 
     obj1.method1(); 
    } 
} 

La siguiente prueba va a crear una maqueta de la clase ejemplo MiObjeto a través de la preparación de la clase que crea la instancia de ella (en este ejemplo yo estoy llamando MiClase) con PowerMock y dejar PowerMockito a trozo el constructor de la clase MiObjeto, a continuación, lo que le permite derivadas la MiObjeto metodo1 instancia() llamada:

@RunWith(PowerMockRunner.class) 
@PrepareForTest(MyClass.class) 
public class MyClassTest { 
    @Test 
    public void testMethod1() {  
     MyObject myObjectMock = mock(MyObject.class); 
     when(myObjectMock.method1()).thenReturn(<whatever you want to return>); 
     PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(myObjectMock); 

     MyClass objectTested = new MyClass(); 
     objectTested.method1(); 

     ... // your assertions or verification here 
    } 
} 

Con que su metodo1 interna() devolverá lo que quiere.

Si te gusta los de una sola línea que puede hacer el código más corto mediante la creación de la maqueta y la línea de código auxiliar:

MyObject myObjectMock = when(mock(MyObject.class).method1()).thenReturn(<whatever you want>).getMock(); 
+1

Problema con este enfoque si uso EclEmma para la cobertura de código es que EclEmma da una cobertura de código de 0% para MyClass si agrego MyClass.class en @PrepareForTest en lugar de la cobertura del código real. Creo que esto es una limitación o error con la herramienta EclEmma. Alguna idea para superar este problema? –

+1

@Vidyasagar Encontré el mismo problema en Sonar/Jacoco. –

Cuestiones relacionadas