Je reçois l'exception suivante pendant l'exécution des tests. J'utilise Mockito pour me moquer. Les indications mentionnées par la bibliothèque Mockito n’aident en rien.
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)
..........
Code de test de DomainTestFactory. Lorsque je lance le test suivant, je vois l'exception
@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;
}
}
Vous vous nidifiez en vous moquant de l'intérieur. Vous appelez getSomeList()
, ce qui vous moque un peu, avant que vous ayez fini de vous moquer de MyMainModel
. Mockito n'aime pas quand tu fais ça.
Remplacer
@Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}
avec
@Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
List<SomeModel> someModelList = getSomeList();
Mockito.when(mainModel.getList()).thenReturn(someModelList);
}
Pour comprendre pourquoi cela pose un problème, vous devez connaître un peu le fonctionnement de Mockito et savoir dans quel ordre les expressions et les instructions sont évaluées en Java.
Mockito ne peut pas lire votre code source, donc pour comprendre ce que vous lui demandez de faire, il dépend beaucoup de l'état statique. Lorsque vous appelez une méthode sur un objet fictif, Mockito enregistre les détails de l'appel dans une liste interne d'appels. La méthode when
lit le dernier de ces invocations dans la liste et enregistre cet appel dans l'objet OngoingStubbing
qu'elle renvoie.
La ligne
Mockito.when(mainModel.getList()).thenReturn(someModelList);
provoque les interactions suivantes avec Mockito:
mainModel.getList()
est appelée,when
est appelée,thenReturn
est appelée sur l'objet OngoingStubbing
renvoyé par la méthode when
.La méthode thenReturn
peut alors ordonner à la maquette reçue via la méthode OngoingStubbing
de gérer tout appel approprié à la méthode getList
pour renvoyer someModelList
.
En fait, comme Mockito ne peut pas voir votre code, vous pouvez également écrire votre moquerie de la manière suivante:
mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);
Ce style est un peu moins clair à lire, surtout que dans ce cas, la null
doit être convertie, mais génère la même séquence d'interactions avec Mockito et aboutira au même résultat que la ligne ci-dessus.
Cependant, la ligne
Mockito.when(mainModel.getList()).thenReturn(getSomeList());
provoque les interactions suivantes avec Mockito:
mainModel.getList()
est appelée,when
est appelée,mock
sur SomeModel
est créé (à l'intérieur de getSomeList()
),model.getName()
est appelée,À ce stade, Mockito est confus. Il pensait que vous vous moquiez de mainModel.getList()
, mais vous lui dites maintenant que vous voulez vous moquer de la méthode model.getName()
. Pour Mockito, il semblerait que vous procédiez comme suit:
when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);
Cela semble idiot de Mockito
car il ne peut pas être sûr de ce que vous faites avec mainModel.getList()
.
Notez que l'appel à la méthode thenReturn
n'est pas arrivé, car la machine virtuelle Java doit évaluer les paramètres de cette méthode avant de pouvoir l'appeler. Dans ce cas, cela signifie qu’il faut appeler la méthode getSomeList()
.
Comme dans le cas de Mockito, le choix de l'état statique est généralement une mauvaise décision de conception, car il peut en résulter des cas de violation du principe de moindre étonnement. Cependant, la conception de Mockito permet des moquer clairs et expressifs, même si cela peut parfois vous surprendre.
Enfin, les versions récentes de Mockito ajoutent une ligne supplémentaire au message d'erreur ci-dessus. Cette ligne supplémentaire indique que vous êtes peut-être dans la même situation que cette question:
3: vous bloquez le comportement d'un autre simulateur à l'intérieur avant l'instruction 'thenReturn' si elle est terminée