Me gustaría probar una clase abstracta. Claro, puedo escribir manualmente un simulacro que hereda de la clase.
¿Puedo hacer esto usando un marco burlón (estoy usando Mockito) en lugar de hacer mi simulacro a mano? ¿Cómo?
java
unit-testing
mocking
abstract-class
mockito
ripper234
fuente
fuente
SomeAbstract spy = spy(SomeAbstract.class);
mock(MyAbstractClass.class, withSettings().useConstructor(arg1, arg2).defaultAnswer(CALLS_REAL_METHODS))
Respuestas:
La siguiente sugerencia le permite probar clases abstractas sin crear una subclase "real": el simulacro es la subclase.
use
Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS)
, luego simule cualquier método abstracto que se invoque.Ejemplo:
Nota: La belleza de esta solución es que no tiene que implementar los métodos abstractos, siempre y cuando nunca se invoquen.
En mi sincera opinión, esto es más ordenado que usar un espía, ya que un espía requiere una instancia, lo que significa que debe crear una subclase de su clase abstracta con instancia.
fuente
Si solo necesita probar algunos de los métodos concretos sin tocar ninguno de los resúmenes, puede usar
CALLS_REAL_METHODS
(vea la respuesta de Morten ), pero si el método concreto bajo prueba llama a algunos de los resúmenes o métodos de interfaz no implementados, esto no funcionará - Mockito se quejará "No se puede llamar al método real en la interfaz de Java".(Sí, es un diseño pésimo, pero algunos marcos, por ejemplo, Tapestry 4, lo fuerzan).
La solución alternativa es revertir este enfoque: use el comportamiento simulado ordinario (es decir, todo se burla / tropeza) y úselo
doCallRealMethod()
para llamar explícitamente el método concreto bajo prueba. P.ejActualizado para agregar:
Para los métodos no nulos, deberá usar
thenCallRealMethod()
en su lugar, por ejemplo:De lo contrario, Mockito se quejará "Se detectó un trozo inacabado".
fuente
Puede lograr esto utilizando un espía (sin embargo, use la última versión de Mockito 1.8+).
fuente
Los marcos de simulación están diseñados para facilitar la simulación de dependencias de la clase que está probando. Cuando utiliza un marco de imitación para simular una clase, la mayoría de los marcos crean dinámicamente una subclase y reemplazan la implementación del método con un código para detectar cuándo se llama a un método y devolver un valor falso.
Al probar una clase abstracta, desea ejecutar los métodos no abstractos del Sujeto bajo prueba (SUT), por lo que un marco de imitación no es lo que desea.
Parte de la confusión es que la respuesta a la pregunta que vinculaste dijo para crear una simulación que se extiende desde tu clase abstracta. No llamaría a una clase como una burla. Un simulacro es una clase que se usa como reemplazo de una dependencia, se programa con expectativas y se puede consultar para ver si se cumplen esas expectativas.
En cambio, sugiero definir una subclase no abstracta de su clase abstracta en su prueba. Si eso resulta en demasiado código, eso puede ser una señal de que su clase es difícil de extender.
Una solución alternativa sería hacer que su caso de prueba sea abstracto, con un método abstracto para crear el SUT (en otras palabras, el caso de prueba usaría el patrón de diseño del Método de plantilla ).
fuente
Intenta usar una respuesta personalizada.
Por ejemplo:
Devolverá la simulación de métodos abstractos y llamará al método real para métodos concretos.
fuente
Lo que realmente me hace sentir mal por burlarme de las clases abstractas es el hecho de que ni el constructor predeterminado YourAbstractClass () se llama (falta super () en simulacro) ni parece que haya alguna forma en Mockito para inicializar las propiedades simuladas por defecto (por ejemplo, Propiedades de lista con ArrayList o LinkedList vacíos).
Mi clase abstracta (básicamente se genera el código fuente de la clase) NO proporciona una inyección de establecimiento de dependencia para elementos de lista, ni un constructor donde inicializa los elementos de lista (que intenté agregar manualmente).
Solo los atributos de clase utilizan la inicialización predeterminada: lista privada dep1 = nueva lista de matriz; Lista privada dep2 = nueva ArrayList
Por lo tanto, NO hay forma de burlarse de una clase abstracta sin usar una implementación de objeto real (por ejemplo, definición de clase interna en clase de prueba unitaria, anulación de métodos abstractos) y espiar el objeto real (que realiza la inicialización de campo adecuada).
Lástima que solo PowerMock ayudaría más aquí.
fuente
Asumiendo que sus clases de prueba están en el mismo paquete (bajo una raíz fuente diferente) que sus clases bajo prueba, simplemente puede crear el simulacro:
y llame a los métodos que desea probar como lo haría con cualquier otro método.
Debe proporcionar expectativas para cada método que se llama con la expectativa de cualquier método concreto que llame al súper método; no estoy seguro de cómo haría eso con Mockito, pero creo que es posible con EasyMock.
Todo lo que está haciendo es crear una instancia concreta
YouClass
y ahorrarle el esfuerzo de proporcionar implementaciones vacías de cada método abstracto.Por otro lado, a menudo me resulta útil implementar la clase abstracta en mi prueba, donde sirve como una implementación de ejemplo que pruebo a través de su interfaz pública, aunque esto depende de la funcionalidad proporcionada por la clase abstracta.
fuente
Puede extender la clase abstracta con una clase anónima en su prueba. Por ejemplo (usando Junit 4):
fuente
Mockito permite burlarse de clases abstractas mediante la
@Mock
anotación:La desventaja es que no se puede usar si necesita parámetros de constructor.
fuente
Puede crear una instancia de una clase anónima, inyectar sus simulacros y luego probar esa clase.
Tenga en cuenta que la visibilidad debe ser
protected
para la propiedadmyDependencyService
de la clase abstractaClassUnderTest
.fuente
PowerMock's
Whitebox.invokeMethod(..)
puede ser útil en este caso.fuente