Lanzar excepciones marcadas de simulacros con Mockito

173

Estoy tratando de que uno de mis objetos burlados arroje una excepción marcada cuando se llama a un método en particular. Estoy intentando lo siguiente.

@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 {
}

Sin embargo, eso produce el siguiente error.

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

Mirando la documentación de Mockito , solo usan RuntimeException, ¿no es posible lanzar Excepciones marcadas de un objeto simulado con Mockito?

Arthur Maltson
fuente

Respuestas:

221

Verifique la API de Java para la Lista .
Se get(int index)declara que el método arroja solo el IndexOutOfBoundExceptionque se extiende RuntimeException.
Estás tratando de decirle a Mockito que arroje una excepción SomeException()que no es válida para ser lanzada por esa llamada de método en particular .

Para aclarar más.
La interfaz de Lista no permite get(int index)que se arroje una Excepción marcada del método y es por eso que Mockito falla.
Cuando cree la Lista simulada, Mockito usará la definición de Lista .class para crear su simulación.

El comportamiento que está especificando con el when(list.get(0)).thenThrow(new SomeException()) no coincide con la firma del método en List API , porque el get(int index)método no arroja,SomeException() por lo que Mockito falla.

Si realmente quieres hacer esto, entonces haz que Mockito arroje un new RuntimeException() o incluso mejor, new ArrayIndexOutOfBoundsException()ya que la API especifica que esa es la única excepción válida que se lanzará.

John Engelman
fuente
Si bien mi código real no estaba usando List, su respuesta también se aplica a esa llamada de método. Me estaba burlando del método equivocado. Gracias.
Arthur Maltson el
2
extra: Mocktio no se quejará si lanzas un método sin tirar, pero también obtendrás esta excepción
dwana
8
Para Kotliners: Kotlin no tiene excepciones marcadas, por lo que normalmente no puede declarar (en la firma de la función) que la función arroja una excepción. Sin embargo, puede anotar la función con Throwsanotaciones para hacer que el compilador genere el mismo código de bytes que declarar tiros en el código Java equivalente. Ver [aquí] ( kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-throws/… ) para más detalles.
Javad Sadeqzadeh
1
Esta verificación se aplica desde el lanzamiento de Mockito 2.11.0 (ver 2.10.3) .
JJD
106

Una solución alternativa es utilizar un willAnswer()método.

Por ejemplo, lo siguiente funciona (y no arroja un, MockitoExceptionpero en realidad arroja un marcado Exceptioncomo se requiere aquí) usando BDDMockito:

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

El equivalente para Mockito simple sería usar el doAnswermétodo

Deepak
fuente
9
o usar willAnswer( invocation -> { throw new Exception("abc msg"); }).given(someObj).someMethod(stringArg1);cuando el método regrese void.
Julien Kronegg
9
o use when (someObj.someMethod (stringArg1)). thenAnswer (invocación -> {throw new Exception ("abc msg");});
Dmitri Algazin
Gran solución, gracias! Para los Kotliners que desean (1) usarlo sin problemas como una función de extensión y (2) poder pasar varios argumentos comowillThrow() normalmente lo permite, he escrito un Gist
David Ferrand
2
o doAnswer desdenhaarman.mockitokotlin2
hmac
6

Tenga en cuenta que, en general, Mockito no permite lanzar excepciones controladas, siempre y cuando la excepción se declara en la firma del mensaje. Por ejemplo, dado

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

interface Foo {
  Bar frob() throws BarException
}

es legal escribir:

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

Sin embargo, si arroja una excepción marcada no declarada en la firma del método, por ejemplo

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

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

Mockito fallará en tiempo de ejecución con el mensaje genérico algo engañoso:

Checked exception is invalid for this method!
Invalid: QuxException

Esto puede llevarlo a creer que las excepciones marcadas en general no son compatibles, pero de hecho Mockito solo está tratando de decirle que esta excepción marcada no es válida para este método .

David Moles
fuente
5

Existe la solución con Kotlin:

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

De donde viene dado

import org.mockito.BDDMockito.given

Kevin ABRIOUX
fuente
1

Esto funciona para mí en Kotlin:

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

Nota: Lanza cualquier excepción definida que no sea Exception ()

Alok Gupta
fuente
Justo lo que estaba buscando, puede lanzar cualquier excepción que no seaException
Naeem Sarfraz