2010-08-12 14 views
68

Estoy usando Mockito en algunas pruebas.Mockito Cómo simular solo la llamada de un método de la superclase

que tienen las siguientes clases:

class BaseService { 
    public void save() {...} 
} 

public Childservice extends BaseService { 
    public void save(){ 
     //some code 
     super.save(); 
    } 
} 

Quiero burlarse sólo la segunda llamada (super.save) de ChildService. La primera llamada debe llamar al método real. ¿Hay una manera de hacer eso?

+0

¿Se puede resolver esto con PowerMockito? – javaPlease42

Respuesta

49

No, Mockito no es compatible con esto.

Esto podría no ser la respuesta que está buscando, pero lo que estamos viendo es un síntoma de no aplicar el principio de diseño:

Favor composition over inheritance

Si extrae una estrategia en lugar de extender una súper clase, el problema se ha ido.

Sin embargo, si no puede cambiar el código, pero debe probarlo de todos modos, y de esta manera incómoda, todavía hay esperanza. Con algunas herramientas de AOP (por ejemplo, AspectJ) puede tejer código en el método de superclase y evitar su ejecución por completo (yuck). Esto no funciona si está utilizando proxies, tiene que usar la modificación del código de bytes (ya sea carga de tiempo de tejido o tiempo de compilación). También existen marcos de burla que también respaldan este tipo de truco, como PowerMock y PowerMockito.

Te sugiero que vayas a la refactorización, pero si eso no es una opción, te espera un poco de diversión.

+2

No veo la violación de LSP. Tengo más o menos la misma configuración que el OP: una clase base DAO con un método findAll() y una subclase DAO que anula el método base llamando a super.findAll() y luego ordenando el resultado. La subclase es sustituible en todos los contextos que aceptan la superclase. ¿Estoy malinterpretando tu significado? –

+1

Eliminaré la observación de LSP (no agrega valor a la respuesta). – iwein

+0

Sí, la herencia apesta y el marco estúpido al que estoy atascado está diseñado con herencia como la única opción. –

66

Si realmente no tiene otra opción para refactorizar, puede simular todos los detalles de la llamada a supermétodo, p.

class BaseService { 

     public void validate(){ 
      fail(" I must not be called"); 
     } 

     public void save(){ 
      //Save method of super will still be called. 
      validate(); 
     } 
    } 

    class ChildService extends BaseService{ 

     public void load(){} 

     public void save(){ 
      super.save(); 
      load(); 
     } 
    } 

    @Test 
    public void testSave() { 
     ChildService spy = Mockito.spy(new ChildService()); 

     // Prevent/stub logic in super.save() 
     Mockito.doNothing().when((BaseService)spy).validate(); 

     // When 
     spy.save(); 

     // Then 
     verify(spy).load(); 
    } 
+1

este código en realidad no evitaría la invocación de super.save(), así que si haces mucho en super.save() tendrías que evitar todas esas llamadas ... – iwein

+0

fantástica solución funciona de maravilla para mí cuando Quiero devolver un valor burlado de un método de superclase para que lo use el niño, fantástico gracias. – Gurnard

+0

Muchas gracias. –

1

crear un paquete protegido (asume clase de prueba en el mismo paquete) método en la subclase que llama al método de superclase y luego llamar a dicho método en el método de la clase sub reemplazado. a continuación, puede establecer expectativas en este método en su prueba mediante el uso del patrón de espía. no es bonito pero ciertamente mejor que tener que lidiar con todas las expectativas del método super en tu prueba

+0

también puedo decir que la composición sobre la herencia es casi siempre mejor, pero a veces es simplemente más simple de usar la herencia. Hasta que java incorpora un mejor modelo de composición , como Scala o Groovy, este siempre será el caso y este problema continuará existiendo – Luke

4

Considera refaccionar el código del método ChildService.save() con un método diferente y prueba ese nuevo método en lugar de probar ChildService.save(), de esta forma evitará llamadas innecesarias al supermétodo.

Ejemplo:

class BaseService { 
    public void save() {...} 
} 

public Childservice extends BaseService { 
    public void save(){ 
     newMethod();  
     super.save(); 
    } 
    public void newMethod(){ 
     //some codes 
    } 
} 
0

La razón es que su clase base no es público-ed, a continuación, Mockito no puede interceptarlo debido a la visibilidad, si cambia la clase base como pública, o @ Override en la subclase (como se público), entonces Mockito puede burlarse de él correctamente.

public class BaseService{ 
    public boolean foo(){ 
    return true; 
    } 
} 

public ChildService extends BaseService{ 
} 

@Test 
@Mock ChildService childService; 
public void testSave() { 
    Mockito.when(childService.foo()).thenReturn(false); 

    // When 
    assertFalse(childService.foo()); 
} 
+6

Este no es el punto. ChildService debe reemplazar a foo() y el problema es cómo simular BaseService.foo() pero no ChildService.foo() –

0

Tal vez la opción más fácil si la herencia que tiene sentido es crear un nuevo método (paquete privado ??) para llamar al super (llamémosle superFindall), espiar la instancia real y luego burlarse de la superFindAll() método en la forma en que quisiste burlarte de la clase padre. No es la solución perfecta en términos de cobertura y visibilidad, pero debería hacer el trabajo y es fácil de aplicar.

public Childservice extends BaseService { 
    public void save(){ 
     //some code 
     superSave(); 
    } 

    void superSave(){ 
     super.save(); 
    } 
} 
Cuestiones relacionadas