jeter les exceptions vérifiées des mocks avec Mockito

173

J'essaie de faire en sorte que l'un de mes objets simulés lève une exception cochée lorsqu'une méthode particulière est appelée. J'essaye ce qui suit.

@Test(expectedExceptions = SomeException.class)
public void throwCheckedException() {
    List<String> list = mock(List.class);
    when(list.get(0)).thenThrow(new SomeException());
    String test = list.get(0);
}

public class SomeException extends Exception {
}

Cependant, cela produit l'erreur suivante.

org.testng.TestException: 
Expected exception com.testing.MockitoCheckedExceptions$SomeException but got org.mockito.exceptions.base.MockitoException: 
Checked exception is invalid for this method!
Invalid: com.testing.MockitoCheckedExceptions$SomeException

En regardant la documentation Mockito , ils utilisent seulement RuntimeException, n'est-il pas possible de lancer des exceptions vérifiées à partir d'un objet fictif avec Mockito?

Arthur Maltson
la source

Réponses:

221

Vérifiez l'API Java pour List .
La get(int index)méthode est déclarée pour lancer uniquement le IndexOutOfBoundExceptionqui s'étend RuntimeException.
Vous essayez de dire à Mockito de lever une exception SomeException()qui n'est pas valide pour être levée par cet appel de méthode particulier .

Pour clarifier davantage.
L' interface List ne fournit pas d'exception vérifiée à get(int index)lever de la méthode et c'est pourquoi Mockito échoue.
Lorsque vous créez la liste simulée, Mockito utilisera la définition de List .class pour créer sa maquette.

Le comportement que vous spécifiez avec le when(list.get(0)).thenThrow(new SomeException()) ne correspond pas à la signature de la méthode dans l'API List , car la get(int index)méthode n'est pas lancéeSomeException() et Mockito échoue.

Si vous voulez vraiment faire cela, demandez à Mockito de lancer un new RuntimeException()ou encore mieux de lancer un new ArrayIndexOutOfBoundsException()car l'API spécifie que c'est la seule exception valide à lever.

John Engelman
la source
Bien que mon vrai code n'utilise pas réellement List, votre réponse s'applique également à cet appel de méthode. Je me moquais de la mauvaise méthode. Je vous remercie.
Arthur Maltson
2
extra: Mocktio ne se plaindra pas si vous lancez une méthode a sans objet jetable, mais vous obtiendrez également cette exception
dwana
8
Pour Kotliners: Kotlin n'a pas vérifié les exceptions, vous ne pouvez donc normalement pas déclarer (dans la signature de la fonction) que la fonction lève une exception. Cependant, vous pouvez annoter la fonction avec une Throwsannotation pour que le compilateur génère le même bytecode que la déclaration de throws dans le code Java équivalent. Voir [ici] ( kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-throws/… ) pour plus de détails.
Javad Sadeqzadeh
1
Cette vérification est appliquée depuis la sortie de Mockito 2.11.0 (voir 2.10.3) .
JJD
106

Une solution de contournement consiste à utiliser une willAnswer()méthode.

Par exemple, ce qui suit fonctionne (et ne lance pas un MockitoExceptionmais jette en fait un vérifié Exceptioncomme requis ici) en utilisant BDDMockito:

given(someObj.someMethod(stringArg1)).willAnswer( invocation -> { throw new Exception("abc msg"); });

L'équivalent de Mockito ordinaire serait d'utiliser la doAnswerméthode

Deepak
la source
9
ou à utiliser willAnswer( invocation -> { throw new Exception("abc msg"); }).given(someObj).someMethod(stringArg1);lorsque la méthode retourne void.
Julien Kronegg
9
ou utilisez when (someObj.someMethod (stringArg1)). thenAnswer (invocation -> {throw new Exception ("abc msg");});
Dmitri Algazin
Excellente solution de contournement, merci! Pour les Kotliners qui veulent (1) utiliser cela de manière transparente comme une fonction d'extension et (2) pouvoir passer plusieurs arguments comme le willThrow()permet normalement, j'ai écrit un Gist
David Ferrand
2
ou doAnswerdenhaarman.mockitokotlin2
hmac
6

Notez qu'en général, Mockito permet de lever des exceptions vérifiées tant que l'exception est déclarée dans la signature du message. Par exemple, étant donné

class BarException extends Exception {
  // this is a checked exception
}

interface Foo {
  Bar frob() throws BarException
}

il est légal d'écrire:

Foo foo = mock(Foo.class);
when(foo.frob()).thenThrow(BarException.class)

Cependant, si vous lancez une exception vérifiée non déclarée dans la signature de la méthode, par exemple

class QuxException extends Exception {
  // a different checked exception
}

Foo foo = mock(Foo.class);
when(foo.frob()).thenThrow(QuxException.class)

Mockito échouera au moment de l'exécution avec le message générique quelque peu trompeur:

Checked exception is invalid for this method!
Invalid: QuxException

Cela peut vous amener à croire que les exceptions vérifiées en général ne sont pas prises en charge, mais en fait Mockito essaie seulement de vous dire que cette exception vérifiée n'est pas valide pour cette méthode .

David Moles
la source
5

Il y a la solution avec Kotlin:

given(myObject.myCall()).willAnswer {
    throw IOException("Ooops")
}

D'où vient

import org.mockito.BDDMockito.given

Kevin ABRIOUX
la source
1

Cela fonctionne pour moi à Kotlin:

when(list.get(0)).thenThrow(new ArrayIndexOutOfBoundsException());

Remarque: lancez toute exception définie autre que Exception ()

Alok Gupta
la source
Juste ce que je cherchais, peut lancer n'importe quelle exception autre queException
Naeem Sarfraz