Download Indice Introducción
Document related concepts
no text concepts found
Transcript
EPS, Informática EPS, Informática Indice JUnit Guía básica de uso http://www.junit.org Introducción Descripción Creación de una clase de prueba para una clase Java. Ejecución de los casos de prueba Manejo de excepciones con JUnit Prueba de los métodos privados Extensiones de JUnit Daniel Bolaños - daniel.bolanos@uam.es EPS, Informática Introducción Se trata de un conjunto de clases que nos permiten realizar pruebas unitarias sobre código Java. Está desarrollado en Java. JUnit es Software de código abierto y está alojado en SourceForge. Página principal: www.junit.org. EPS, Informática Descripción JUnit consta de varios paquetes (packages) de clases Paquetes para construir los casos de prueba – junit.framework, contiene las clases básicas de junit que utilizaremos para construir los casos de prueba. – junit.extensions, contiene clases que extienden la funcionalidad de las clases básicas. Paquetes para ejecutar los casos de prueba – junit.textui, permite ejecutar los casos de prueba en una interfaz de texto. – junit,awtui, proporciona una interfaz gráfica AWT para la ejecución de los casos de prueba. – junit.swingui, proporciona una interfaz gráfica Swing para la ejecución de los casos de prueba. EPS, Informática EPS, Informática Paquete junit.framework Paquete junit.framework, clase Assert Nos proporciona un conjunto de clases para construir nuestros casos de prueba, las más importantes son: //Condiciones de igualdad static void assertEquals(boolean expected, boolean actual) static void assertEquals(float expected, float actual, float delta) Assert, proporciona una serie de métodos estáticos para comprobar el cumplimiento de condiciones sobre el código a probar. TestCase, representa un conjunto de pruebas sobre una clase, permite realizar múltiples pruebas sobre sus métodos. TestSuite, permite agrupar diferentes objetos TestCase para su ejecución conjunta. TestResult, permite almacenar los resultados de la ejecución de uno o varios TestCase, de esta forma conoceremos los resultados de las pruebas. Todos sus métodos son estáticos. Posee métodos para probar todo tipo de condiciones. //Condiciones booleanas static void assertTrue(boolean condition) static void assertFalse(boolean condition) //Condiciones sobre objetos static void assertNull(java.lang.Object object) static void assertNotNull(java.lang.Object object) //Condiciones sobre referenacias a objetos static void assertSame(java.lang.Object expected, java.lang.Object actual) static void assertNotSame(java.lang.Object expected, java.lang.Object actual) Contiene un método fail para indicar de forma explícita que el test ha fallado. static void fail(java.lang.String message) EPS, Informática EPS, Informática Paquete junit.framework, clase TestCase Paquete junit.framework, clase TestSuite Representa el conjunto de pruebas que se van a realizar sobre los métodos de una clase. Hereda de Assert, por lo que posee todos sus métodos para la comprobación de condiciones. Implementa la interfaz Test. La forma de utilizar esta clase es heredando de ella para construir nuestras clases de prueba. Sus métodos más importantes son: //Ejecuta los métodos de test (todos los que comienzan por “test”) void run(TestResult result) //Realiza todas las inicializaciones necesarias para la prueba protected void setUp() //Libera todos los recursos utilizados durante la prueba protected void tearDown() Representa una conjunto de casos de prueba. Implementa la interfaz Test, por tanto implementa el método run para la ejecución de los tests. void run(TestResult result) Existen varias formas de utilizarlo: //Creamos un TestSuite y añadimos varios métodos de prueba para ser ejecutados TestSuite suite= new TestSuite(); suite.addTest(new MathTest("testAdd")); suite.addTest(new MathTest("testDivideByZero")); //Creamos un TestSuite a partir de los métodos de test de una clase, es decir //aquellos que comienzan por “test” y no tienen argumentos, simplemente //pasamos la clase y todos los métodos de test se extraen automáticamente para //formar parte del TestSuite TestSuite suite= new TestSuite(MathTest.class); EPS, Informática Creación de una clase de prueba para una clase Java EPS, Informática Ejemplo: prueba de la clase java.util.Vector package junit.samples; Para realizar la prueba de una clase Java con JUnit debemos dar los siguientes pasos 1. 2. 3. 4. import junit.framework.*; import java.util.Vector; Creamos una clase que herede de TestCase, esta clase se encargará de realizar las pruebas sobre nuestra clase a probar, y seguirá la nomenclatura NombreClaseProbarTest. Sobrescribimos el método setUp para hacer las inicializaciones necesarias para la prueba, es decir, conexiones de red, apertura de ficheros, instanciación de clases, etc. Sobreescribimos el método tearDown, que se encargará de liberar todos los recursos utilizados durante la prueba. Para cada método público de la clase a probar definiremos un método de test, que seguirá la nomenclatura testMetodoProbar. Este método debe realizar todas las pruebas necesarias sobre el método a probar, lo invocará de diferentes formas y comprobará condiciones sobre su retorno mediante el uso de asserts, heredados por TestCase de su clase padre Assert. public class VectorTest extends TestCase { protected Vector fEmpty; protected Vector fFull; public static void main (String[] args) { junit.textui.TestRunner.run (suite()); } protected void setUp() { fEmpty= new Vector(); fFull= new Vector(); fFull.addElement(new Integer(1)); fFull.addElement(new Integer(2)); fFull.addElement(new Integer(3)); } EPS, Informática Ejemplo: prueba de la clase java.util.Vector EPS, Informática Ejemplo: prueba de la clase java.util.Vector public void testElementAt() { Integer i= (Integer)fFull.elementAt(0); assertTrue(i.intValue() == 1); public static Test suite() { return new TestSuite(VectorTest.class); } public void testCapacity() { int size= fFull.size(); for (int i= 0; i < 100; i++) fFull.addElement(new Integer(i)); assertTrue(fFull.size() == 100+size); } public void testClone() { Vector clone= (Vector)fFull.clone(); assertTrue(clone.size() == fFull.size()); assertTrue(clone.contains(new Integer(1))); } public void testContains() { assertTrue(fFull.contains(new Integer(1))); assertTrue(!fEmpty.contains(new Integer(1))); } try { fFull.elementAt(fFull.size()); } catch (ArrayIndexOutOfBoundsException e) { return; } fail("Should raise an ArrayIndexOutOfBoundsException"); } public void testRemoveAll() { fFull.removeAllElements(); fEmpty.removeAllElements(); assertTrue(fFull.isEmpty()); assertTrue(fEmpty.isEmpty()); } public void testRemoveElement() { fFull.removeElement(new Integer(3)); assertTrue(!fFull.contains(new Integer(3)) ); } } EPS, Informática EPS, Informática Ejecución de los casos de prueba Ejecución de los casos de prueba Ejemplo: vamos a probar las clases Vector, String y StringBuffer, mediante sus correspondientes clases de prueba. A la hora de ejecutar nuestros casos de prueba podemos utilizar una de estas tres clases – JUnit.textui.TestRunner, esta clase permite ejecutar las clases de prueba a través del método run. //Inicialmente creamos el obeto TestSuite y le añadimos los test de //las diferentes clases de prueba TestSuite testSuite = new TestSuite(VectorTest.class); testSuite.addTestSuite(StringTest.class); testSuite.addTestSuite(StringBufferTest.class); static TestResult run (Test test); Este método recibe como argumento un objeto que implemente la interfaz Test (TestCase y TestSuite la implementan) y devuelve un objeto de tipo TestResult con la información que se ha obtenido como resultado de la ejecución de los métodos de prueba contenidos en dicho objeto. – JUnit.awtui.TestRunner, esta clase es equivalente a la anterior, sólo que presenta una interfaz AWT. – JUnit.swingtui.TestRunner, esta clase es equivalente a la anterior, sólo que presenta una interfaz Swing. //Ejecutamos los test y almacenamos el resultado TestResult testResult = junit.textui.TestRunner.run(testSuite); Una vez finalizada la ejecución de las pruebas podemos utilizar la información contenida en el testResult para generar documentación de forma automática. EPS, Informática Manejo de excepciones con JUnit Excepciones esperadas: veámoslo con un ejemplo public void testMetodoQueLanzaExcepcion() { try { //Invocamos el método de forma que deba lanzar una excepción metodoQueLanzaExcepcion(null); //Si el flujo de control pasa por aquí es porque la excepción //no saltó, entonces indicamos que ha habido un fallo fail(“El método debería haber lanzado una excepción”); EPS, Informática Manejo de excepciones con JUnit Otra forma de manejar las excepciones esperadas es haciendo uso de la clase ExceptionTestCase, perteneciente al paquete junit.extensions. Esta clase hereda de TestCase y espera que una excepción de un tipo dado sea lanzada, veamos un ejemplo: //Heredamos de ExceptionTestCase en lugar de TestCase public class MiClaseTest() extends ExceptionTestCase { } catch (RunTimeException e) { //A modo de documentación, para indicar que la prueba ha //tenido éxito el flujo de control ha de pasar por aquí assertTrue(true); } public MiClaseTest(String name, Class exception) { super(name,exception); } //Debemos llamar al constructor de la superclase con la clase de //la excepción que puede lanzar el método a probar MiClaseTest new MiClaseTest(“nombre", ExcepcionLanzada.class); EPS, Informática Manejo de excepciones con JUnit EPS, Informática Manejo de excepciones con JUnit public class ExceptionTestCase extends TestCase { Class fExpected; public ExceptionTestCase(String name, Class exception) { super(name); fExpected= exception; } protected void runTest() throws Throwable { try { super.runTest(); } catch (Exception e) { if (fExpected.isAssignableFrom(e.getClass())) return; else throw e; } fail("Expected exception " + fExpected); } Excepciones no esperadas: en este caso no tenemos que preocuparnos de capturar las excepciones, simplemente declararemos nuestro método de prueba de forma que pueda lanzar todas aquellas excepciones que pueda lanzar el método a probar. Veámoslo con un ejemplo. public void testMetodoQueLanzaExcepcion() throws FileNotFoundException { //Invocamos el método de forma que no deba fallar FileInputStream entrada = new FileInputStream(“datos.txt”); } } EPS, Informática EPS, Informática Prueba de los métodos privados Prueba de los métodos privados Primer enfoque: no probarlos directamente, sino a través de los métodos públicos que los utilizan. El problema es que esto no es tan sencillo ya que garantizar una cobertura en la prueba de un método a través de la cobertura en la prueba de otro que le llama, puede resultar bastante complejo. Cambiar el atributo de privacidad del método “private” por el de acceso desde el mismo paquete, es decir, sin atributo. Estás sacrificando la modularidad y la ocultación de datos en favor de la calidad de las pruebas. Utilizar clases anidadas, se trata de una solución parecida a la anterior, el problema es que mezclamos el código de la clase a probar con el código de la prueba, y esto conlleva problemas en el mantenimiento. Utilizar reflection, mediante la API java.lang.reflect es posible saltarse el mecanismo de protección de acceso de la máquina virtual de Java, mediante la clase junitx.util.PrivateAccessor podemos manipular los métodos privados de cualquier clase. Ventajas Separación limpia entre el código de test y el código de producción. No es necesario anidar la clase de test dentro de la clase a testear. No es necesario modificar la API de la clase a testear. Inconvenientes Es necesario utilizar la API de reflection dentro de nuestras clases de prueba. Puesto que al utilizar reflection, te refieres a los métodos como Strings, si cambiamos el nombre del método, hay que cambiar el código de la prueba. EPS, Informática Extensiones de JUnit JTestCase Debido a la gran difusión de JUnit, existen multitud de paquetes que extienden su funcionalidad, entre otras cosas permiten: Generación automática de documentación sobre los resultados de la prueba (JUnitReport). Generación automática de los casos de prueba a partir del código fuente. Tests sobre los tests. Separación de los datos y el código en los casos de prueba mediante documentos XML (JTestCase). Pruebas concurrentes. Prueba de excepciones (paquete junit.extensions). Pruebas distribuidas utilizando HTTP (JHttpUnit). Pruebas de sitios WEB (JWebUnit). Pruebas de interfaces de usuario (JFCUnit para Swing) ... EPS, Informática Es un framework de código abierto, reside en SourceForge, http://jtestcase.sourceforge.net/ Permite almacenar en ficheros XML los datos con los que se ejecutarán los casos de prueba, de esta forma conseguimos: – Posibilidad de ampliar el conjunto de datos de entrada a los tests sin modificar el código de las pruebas. – La persona que diseña los conjuntos de datos de entrada no tiene porque saber como está hecho el código de las pruebas. EPS, Informática EPS, Informática JTestCase JTestCase Ejemplo: deseamos probar la clase Calculator Un fichero xml contiene información acerca de los parámetros con los que se han de probar nuestras clases y métodos. package sample; public class Calculator { public int calculate(int v1, int v2, String opt) throws Exception { if (opt.equals("+")) return v1 + v2; else if (opt.equals("-")) return v1 - v2; else if (opt.equals("*")) return v1 * v2; else if (opt.equals("/")) return (int)(v1/v2); else throw new Exception("Unknown operator " + opt + "!"); } public String func2() throws Exception { return "Who cares!"; } } <?xml version="1.0" encoding="UTF-8" ?> <tests xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xsi:noNamespaceSchemaLocation="config/jtestcase.xsd"><class name="CalculatorTest"><!-- global params --> <params> <param name="hashInit" type="java.util.Hashtable" /> </params> <method name="testCalculate" test-case="positive-add"> <params> <param name="var1" type="int">10</param> <param name="var2" type="int">20</param> <param name="opt" type="java.lang.String">+</param> </params><asserts> <assert name="result" type="int“ action="EQUALS">30</assert> </asserts> </method>- EPS, Informática JTestCase <method name="testCalculate" test-case="positive-minus"> <params> <param name="var1" type="int">10</param> <param name="var2" type="int">20</param> <param name="opt" type="java.lang.String">-</param> </params><asserts> <assert name="result" type="int" action="EQUALS">-10</assert> </asserts> </method><method name="testCalculate" test-case="negative-wrong-opt"><params> <param name="var1" type="int">10</param> <param name="var2" type="int">20</param> <param name="opt" type="java.lang.String">ccxx</param> </params><asserts> <assert name="" type="java.lang.String" action="EQUALS">N/A: should throw Exception</assert> </asserts> </method><method name="testFunc2" test-case="positive"> <params /> <asserts /> </method> </class> </tests>