java - examples - Troquelado inacabado detectado en Mockito
mockito verify (1)
Estoy siguiendo la excepción al ejecutar las pruebas. Estoy usando Mockito para burlarse. Los consejos mencionados por la biblioteca de Mockito no están ayudando.
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
..........
Código de prueba de DomainTestFactory. Cuando ejecuto la siguiente prueba, veo la excepción
@Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}
private List<SomeModel> getSomeList() {
SomeModel model = Mockito.mock(SomeModel.class);
Mockito.when(model.getName()).thenReturn("SomeName"); --> Line 276
Mockito.when(model.getAddress()).thenReturn("Address");
return Arrays.asList(model);
}
public class SomeModel extends SomeInputModel{
protected String address;
protected List<SomeClass> properties;
public SomeModel() {
this.Properties = new java.util.ArrayList<SomeClass>();
}
public String getAddress() {
return this.address;
}
}
public class SomeInputModel{
public NetworkInputModel() {
this.Properties = new java.util.ArrayList<SomeClass>();
}
protected String Name;
protected List<SomeClass> properties;
public String getName() {
return this.Name;
}
public void setName(String value) {
this.Name = value;
}
}
Estás anidando burlándose dentro de burla. getSomeList()
a getSomeList()
, que hace burlas, antes de que MyMainModel
la burla de MyMainModel
. A Mockito no le gusta cuando haces esto.
Reemplazar
@Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}
con
@Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
List<SomeModel> someModelList = getSomeList();
Mockito.when(mainModel.getList()).thenReturn(someModelList);
}
Para entender por qué esto causa un problema, necesita saber un poco sobre cómo funciona Mockito, y también saber en qué orden se evalúan las expresiones y los enunciados en Java.
Mockito no puede leer su código fuente, por lo que para averiguar lo que le está pidiendo que haga, depende mucho del estado estático. Cuando llama a un método en un objeto simulado, Mockito registra los detalles de la llamada en una lista interna de invocaciones. El método when
lee la última de estas invocaciones fuera de la lista y registra esta invocación en el objeto OngoingStubbing
que devuelve.
La línea
Mockito.when(mainModel.getList()).thenReturn(someModelList);
causa las siguientes interacciones con Mockito:
- Se
mainModel.getList()
métodomainModel.getList()
, - Método estático
when
se llama, - Método
thenReturn
sethenReturn
en el objetoOngoingStubbing
devuelto por el métodowhen
.
El método thenReturn
puede entonces instruir el simulacro recibido a través del método OngoingStubbing
para manejar cualquier llamada adecuada al método getList
para devolver someModelList
.
De hecho, como Mockito no puede ver tu código, también puedes escribir tu burla de la siguiente manera:
mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);
Este estilo es algo menos claro de leer, especialmente porque en este caso el null
tiene que ser lanzado, pero genera la misma secuencia de interacciones con Mockito y logrará el mismo resultado que la línea anterior.
Sin embargo, la línea
Mockito.when(mainModel.getList()).thenReturn(getSomeList());
causa las siguientes interacciones con Mockito:
- Se
mainModel.getList()
métodomainModel.getList()
, - Método estático
when
se llama, - Se crea una nueva
mock
deSomeModel
(dentro degetSomeList()
), - Se
model.getName()
métodomodel.getName()
,
En este punto, Mockito se confunde. Pensó que se estaba burlando de mainModel.getList()
, pero ahora le está diciendo que quiere burlarse del método model.getName()
. Para Mockito, parece que estás haciendo lo siguiente:
when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);
Esto se ve ridículo para Mockito
ya que no puede estar seguro de lo que estás haciendo con mainModel.getList()
.
Tenga en cuenta que no llegamos a la thenReturn
método thenReturn
, ya que la JVM necesita evaluar los parámetros de este método antes de poder llamar al método. En este caso, esto significa llamar al método getSomeList()
.
En general, es una mala decisión de diseño confiar en el estado estático, como lo hace Mockito, porque puede conducir a casos en los que se viola el Principio del menor asombro. Sin embargo, el diseño de Mockito crea una burla clara y expresiva, incluso si a veces provoca asombro.
Finalmente, las versiones recientes de Mockito agregan una línea adicional al mensaje de error anterior. Esta línea adicional indica que puede estar en la misma situación que esta pregunta:
3: está anulando el comportamiento de otro simulacro dentro antes de ''thenReturn'' instrucción si se completa