mock method examples java unit-testing mocking mockito

java - method - Cómo burlarse de una cadena usando mockito?



mockito spy (14)

Es un requisito del proyecto que la unidad pruebe el porcentaje de cobertura pero debe ser mayor que un valor determinado. Para lograr dicho porcentaje de cobertura, las pruebas deben cubrir el bloque catch en relación con UnsupportedEncodingException.

¿Cuál es ese objetivo de cobertura dado? Algunas personas dirían que disparar al 100% de cobertura no siempre es una buena idea .

Además, esa no es forma de comprobar si se ejecutó o no un bloqueo catch. La forma correcta es escribir un método que provoque la excepción y hacer que la observación de la excepción arroje el criterio de éxito. Haga esto con la anotación @Test de JUnit agregando el valor "esperado":

@Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() { new ArrayList<Object>().get(1); }

Necesito simular un escenario de prueba en el que llamo al método getBytes() de un objeto String y obtengo una UnsupportedEncodingException.

Intenté lograr eso usando el siguiente código:

String nonEncodedString = mock(String.class); when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));

El problema es que cuando ejecuto mi caso de prueba obtengo una excepción MockitoException que dice que no puedo simular una clase java.lang.String.

¿Hay alguna forma de burlar un objeto String usando mockito o, alternativamente, una forma de hacer que mi objeto String arroje una UnsupportedEncodingException cuando llamo al método getBytes?

Aquí hay más detalles para ilustrar el problema:

Esta es la clase que quiero probar:

public final class A { public static String f(String str){ try { return new String(str.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // This is the catch block that I want to exercise. ... } } }

Esta es mi clase de prueba (estoy usando JUnit 4 y mockito):

public class TestA { @Test(expected=UnsupportedEncodingException.class) public void test(){ String aString = mock(String.class); when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error.")); A.f(aString); } }


¿Has intentado pasar un charsetName no válido a getBytes (String)?

Podría implementar un método de ayuda para obtener el CharsetName, y anular ese método dentro de su prueba a un valor sin sentido.


¿Qué tal si creas una String con un nombre de codificación incorrecto? Ver

public String(byte bytes[], int offset, int length, String charsetName)

Mocking String es casi seguro que es una mala idea.


A partir de su documentación, JDave no puede eliminar los modificadores "finales" de las clases cargadas por el cargador de clases bootstrap. Eso incluye todas las clases de JRE (de java.lang, java.util, etc.).

Una herramienta que te permite JMockit cualquier cosa es JMockit .

Con JMockit, su prueba se puede escribir como:

import java.io.*; import org.junit.*; import mockit.*; public final class ATest { @Test(expected = UnsupportedOperationException.class) public void test() throws Exception { new Expectations() { @Mocked("getBytes") String aString; { aString.getBytes(anyString); result = new UnsupportedEncodingException("Parsing error."); } }; A.f("test"); } }

asumiendo que la clase "A" completa es:

import java.io.*; public final class A { public static String f(String str) { try { return new String(str.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new UnsupportedOperationException(e); } } }

De hecho, ejecuté esta prueba en mi máquina. (Observe que envolví la excepción comprobada original en una excepción de tiempo de ejecución).

Utilicé burlas parciales a través de @Mocked("getBytes") para evitar que JMockit se burlara de todo en la clase java.lang.String (imagínense lo que eso podría causar).

Ahora, esta prueba realmente es innecesaria, porque "UTF-8" es un juego de caracteres estándar, requerido para ser soportado en todos los JRE. Por lo tanto, en un entorno de producción, el bloque catch nunca se ejecutará.

Sin embargo, la "necesidad" o el deseo de cubrir el bloque de captura sigue siendo válido. Entonces, ¿cómo deshacerse de la prueba sin reducir el porcentaje de cobertura? Aquí está mi idea: insertar una línea con assert false; como la primera declaración dentro del bloque catch, y hacer que la herramienta Cobertura del código ignore todo el bloque catch al informar medidas de cobertura. Este es uno de mis "elementos TODO" para la Cobertura JMockit. 8 ^)


Como otros han indicado, no puedes usar Mockito para burlar una clase final. Sin embargo, el punto más importante es que la prueba no es especialmente útil porque solo está demostrando que String.getBytes() puede lanzar una excepción, lo que obviamente puede hacer. Si tiene muchas ganas de probar esta funcionalidad, creo que podría agregar un parámetro para la codificación a f() y enviar un valor incorrecto a la prueba.

Además, está causando el mismo problema para la persona que llama de Af() porque A es final y f() es estático.

Este artículo podría ser útil para convencer a sus compañeros de trabajo de ser menos dogmáticos sobre la cobertura del código del 100%: cómo fallar con una cobertura del 100% de la prueba .


El problema es que la clase String en Java está marcada como final, por lo que no puede simular que está utilizando frameworks de burla tradicionales. De acuerdo con las preguntas frecuentes de Mockito , esta es una limitación de ese marco también.



Mockito no puede burlarse de las clases finales. JMock, combinado con una biblioteca de JDave puede. Aquí hay instrucciones .

JMock no hace nada especial para las clases finales aparte de confiar en la biblioteca JDave para desfinalizar todo en la JVM, por lo que puedes experimentar con el uso del desfinalizador de JDave y ver si Mockito se burlará de él.


Puede cambiar su método para tomar la interfaz CharSequence :

public final class A { public static String f(CharSequence str){ try { return new String(str.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { // This is the catch block that I want to exercise. ... } } }

De esa forma, aún puedes pasar String, pero puedes burlarte de la forma que quieras.


Quizás Af (String) debería ser Af (CharSequence) en su lugar. Puedes burlarte de una CharSequence.


Si puedes usar JMockit, mira la respuesta de Rogério.

Si y solo si su objetivo es obtener cobertura de código pero no simular en realidad el aspecto del UTF-8 que falta en el tiempo de ejecución, puede hacer lo siguiente (y que no puede o no quiere usar JMockit):

public static String f(String str){ return f(str, "UTF-8"); } // package private for example static String f(String str, String charsetName){ try { return new String(str.getBytes(charsetName)); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e); } } public class TestA { @Test(expected=IllegalArgumentException.class) public void testInvalid(){ A.f(str, "This is not the encoding you are looking for!"); } @Test public void testNormal(){ // TODO do the normal tests with the method taking only 1 parameter } }


Si tiene un bloque de código que nunca se puede ejecutar realmente, y un requisito de gestión para tener una cobertura de prueba del 100%, entonces algo tendrá que cambiar.

Lo que podría hacer es hacer que la codificación de caracteres sea una variable miembro, y agregar un constructor privado de paquetes a su clase que le permita pasarla. En su prueba de unidad, podría llamar al nuevo constructor, con un valor sin sentido para la codificación de caracteres .


Si todo lo que vas a hacer en tu bloque catch es lanzar una excepción en tiempo de ejecución, entonces puedes ahorrarte algo de tipeo simplemente usando un objeto Charset para especificar el nombre de tu juego de caracteres.

public final class A{ public static String f(String str){ return new String(str.getBytes(Charset.forName("UTF-8"))); } }

De esta forma, no se detecta una excepción que nunca sucederá solo porque el compilador se lo indique.


También puede usar la extensión Mockito de PowerMock para simular las clases / métodos finales incluso en clases del sistema como String. Sin embargo, también recomendaría no burlarse de getBytes en este caso y tratar de configurar su expectativa para que una cadena real sea poblada con los datos esperados.