PRUEBAS DE SOFTWARE de tipo xUnit
PRUEBAS DE SOFTWARE de tipo xUnit Mayo de 2008
1
PRUEBAS DE SOFTWARE de tipo xUnit
CONCEPTOS .....................................................................................................4 DEFINICIONES ................................................................................................6
ESTRATEGIA MÍNIMA DE AUTOMATIZACION DE TESTS ......................................................6 Proceso de desarrollo ..................................................................................6 Pruebas de usuario .....................................................................................6 Unit Tests ..................................................................................................6 Diseñando para testeabilidad........................................................................6 Organización de los tests .............................................................................6
TEST SMELLS (SÍNTOMAS) ....................................................................................6 En proyectos ..............................................................................................6 En el comportamiento .................................................................................6 En el código ...............................................................................................6
OBJETIVOS, FILOSOFÍA Y PRINCIPIOS DE LA AUTOMATIZACIÓN ...................7 OBJETIVOS ......................................................................................................7
Mejorar la calidad .......................................................................................7 Entender al SUT .........................................................................................7 Reducir los riesgos ......................................................................................7 Tests fáciles de ejecutar ..............................................................................7 Tests fáciles de escribir y mantener ...............................................................8 Tests con mínimo mantenimiento ante evolución del sistema ............................8
FILOSOFÍA.......................................................................................................8 Escribir tests antes o después del código a probar ...........................................8 Probar uno a uno o todo a la vez...................................................................8 Probar desde afuera hacia adentro o desde adentro a afuera.............................8 Probar comportamiento o estados .................................................................8 Establecer condiciones de prueba por test o condiciones para todos ...................8
PRINCIPIOS .....................................................................................................9 ESTRATEGIA ..................................................................................................10
QUÉ CLASE DE TEST AUTOMATIZAR.........................................................................10 CUÁLES HERRAMIENTAS UTILIZAR PARA LA AUTOMATIZACIÓN...........................................11 CÓMO ADMINISTRAR LAS CONDICIONES DE PRUEBA......................................................12 CÓMO ASEGURAR QUE EL SISTEMA SERÁ EFICIENTEMENTE PROBADO...................................13
Fake Object .............................................................................................14 Test Stub y Mock Object ............................................................................14 Pruebas por capas....................................................................................15
XUNIT............................................................................................................16 DESCRIPCIÓN DE LAS PAUTAS INICIALES (REQUERIMIENTOS)...........................................16 DISEÑO DE JUNIT ............................................................................................16
CONDICIONES DE PRUEBA ............................................................................20
2
PRUEBAS DE SOFTWARE de tipo xUnit
CONDICIONES TRANSITORIAS ..............................................................................20 Formas de establecimiento.........................................................................20
CONDICIONES PERSISTENTES...............................................................................20 Desventajas.............................................................................................20 Soluciones ...............................................................................................20 Test encadenados .....................................................................................20
VERIFICACIÓN DE RESULTADOS....................................................................22 VERIFICACIONES BÁSICAS...................................................................................22
Comparación de objetos de tipo ValueObjects ...............................................22 Test de Objetos que crean otros objetos.......................................................24 Verificación de comportamiento ..................................................................25 Verificación de estado................................................................................25
LAS PRUEBAS COMO CRITERIO DE DISEÑO ...................................................26 EJEMPLO.......................................................................................................26
UTILIZANDO PLUGINS...................................................................................27 TESTDOUBLE..................................................................................................27
Creación y configuración ...........................................................................27 Instalación...............................................................................................27 MockObject..............................................................................................29 Subclases ................................................................................................30 SpyObject................................................................................................31 FakeObject ..............................................................................................34
PRUEBAS CON BASES DE DATOS....................................................................35 ORGANIZACIÓN DE LOS TESTS DE UN PROYECTO..........................................36
ESTRATEGIAS .................................................................................................36 TestClass por clase ...................................................................................36 TestClass por funcionalidad ........................................................................37 TestClass por condicón de prueba ...............................................................37
HERRAMIENTAS.............................................................................................39 PRUEBAS DE LAS DISTINTAS CAPAS.........................................................................39 FRAMEWORKS.................................................................................................39
JUnit .......................................................................................................39 ServletUnit ..............................................................................................39 HttpUnit ..................................................................................................39 Watir ......................................................................................................40
REFERENCIAS ................................................................................................41
3
PRUEBAS DE SOFTWARE de tipo xUnit
Conceptos • Verificación automatizada: contribuye con aumento de productividad, mejora
de la calidad y mantiene al software robusto a lo largo del proceso de desarrollo
• Realimentación: provee información a lo largo del proceso de desarrollo que es utilizada para realizar cambios y agregar nuevas funcionalidades con mayor confianza.
• Testing: pruebas tradicionales no sirven para mejorar la calidad sino para medirla al ser ejecutadas en forma tardía. Se propondrán pruebas tempranas de unidad, integración y funcionalidad.
• Pruebas de Programadores: de servicios y funcionales. Fomentar las pruebas como parte de las actividades de estos roles crea la cultura de las pruebas como condición indispensable del proceso de desarrollo.
• Automatización: existen sistemas que facilitan la automatización pero cuentan con debilidades. El problema de la fragilidad de los tests utilizando estas herramientas (ej. robots) ha generado opiniones a favor y en contra.
o Comportamiento: cambios a los productos por cambios en requerimientos hacen que las pruebas programadas fallen
o Interfaz: las pruebas basadas en las interfaces de usuario fallan ante cambios menores de las mismas. Las arquitecturas en capas han resuelto algunos problemas de este tipo al desacoplarlas del negocio, pero introdujeron un componente difícil de probar, HTML y el browser cliente.
o Datos: como precondiciones para que el test ejecute. La dependencia de los datos en el sistema pueden hacer que fallen los test.
o Contexto: afecta a los tests de las más diversas formas haciéndolos irrepetibles. Dependen de la fecha, hora, accesos a servidores, etc.
• Uso de automatización de los tests
o Como especificación: a partir del modo de uso de los componentes y su integración al sistema en desarrollo.
o Test Driven Development: conduciendo el diseño a partir de la interacción con los test.
• Patrones: hay suficiente experiencia en el uso de las técnicas de pruebas utilizando xUnit, las cuales fueron agrupadas como patrones de estrategias, diseño y codificación de tests.
• Terminología (ver figura 1) o SUT (system ander test): sistema bajo test
o Fixture: condiciones para que el test sea ejecutado
4
PRUEBAS DE SOFTWARE de tipo xUnit
o DOC (dependend on component): componente del que se depende
o xUnit: familia de frameworks sobre los que se diseña, programa y ejecutan tests.
Figura 1
Figura extraída de [1]
5
PRUEBAS DE SOFTWARE de tipo xUnit
Definiciones ESTRATEGIA MÍNIMA DE AUTOMATIZACION DE TESTS
Proceso de desarrollo • Pruebas funcionales definidas a partir de la especificación de requerimientos
• Tests unitarios y de integración cubriendo todo el código
• Testeo continuo como práctica de integración
Pruebas de usuario • Pruebas de aceptación
Unit Tests • Test simples que cubren las instancias de clases en diferentes escenarios
• Condiciones de pruebas diseñadas
• Resultados verificados
Diseñando para testeabilidad Criterios de buen diseño que entre otras cosas facilitan las pruebas de los diferentes componentes a partir del desacoplamiento. Arquitecturas en capas, inversión en la cadena de dependencia, código clausurado ante cambios, segregación de interfaces y dependencias no cíclicas son algunas de estas buenas prácticas.
Organización de los tests Distribución de los tests en conjuntos según los métodos de las clases o según las condiciones de ejecución por ejemplo.
TEST SMELLS (SÍNTOMAS) La mala calidad de los tests se ponen de manifiesto de distintas formas.
En proyectos Afectan a la funcionalidad, calidad, recursos y costo. Se evidencian a partir de métricas tales como el nro de veces que deben ser modificados los tests, el nro de errores encontrados en las pruebas de funcionalidad realizadas por usuarios (aceptación).
En el comportamiento Causan las fallas de los tests o directamente no compilan. Dependen y son frágiles frente a cambios en interfaces, datos, funcionalidad, fecha, hora, accesos a servidores, etc.
En el código Código escrito utilizando malas prácticas, las cuales son reconocidas por inspección.
6
PRUEBAS DE SOFTWARE de tipo xUnit
Objetivos, filosofía y principios de la automatización
Escusaspara no probar
Clientes no entienden de pruebas lo qua a veces genera la tentación de que escribir código para probar es opcional
El cliente no paga por el código de prueba
Escribir pruebas es duro
Esto generó la necesidad de convertir a las pruebas an automáticas, fáciles de mantener, vincularlas al desarrollo, robustas y efectivas.
Economía de la automatización de los tests
gráfico
OBJETIVOS • Mejorar la calidad
• Entender al SUT
• Reducir los riesgos
• Tests fáciles de ejecutar
• Tests fáciles de escribir y mantener
• Tests con mínimo mantenimiento ante evolución del sistema
Mejorar la calidad Validación (construimos el producto correcto). Test como especificación refleja cómo será usado el SUT. Prototipos de interfaces ayudan a entender el comportamiento en el ambiente aperacional. Vaildación de requerimientos ambiguos y contradictorios.
Verificación (producto construido correctamente). Previene errores, no los detecta sino que evita su introducción. Testeo continuo -> todos los tests ok antes de checkin. Localización de defectos, Unittest + Tests de Usuarios. Si los útimos fallan -> código no probado por unittest.
Entender al SUT Documentación-> SelfTest -> Resultados Esperados
Reducir los riesgos Mejor documentación + previene errores + mantiene último release funcionando y actualizado (testeo continuo = UnitTest + Ctrol. Versión)
Tests fáciles de ejecutar Fácil -> se corren
No Fácil -> NO se corren
7
PRUEBAS DE SOFTWARE de tipo xUnit
Fácil
o Automáticos: edición + compilación + linkeo + ejecución (make, ant, etc)
o Selfchecking: Hollywood Principle, si falla la plataforma avisa
o Repetibles: mismos resultados en múltiples ejecuciones sin intervención manual
o Independientes: posible ejecución aislada
Tests fáciles de escribir y mantener o Centrado en testing y no en código
o No invasivo (no cbios. Al SUT)
o Fácil de leer
o Simples, prueba de una condición a la vez
o Expresivos, desde el negocio por ejemplo
o Separación de incumbencias, 1) separado del código productivo, 2) prueba de aspectos por separado
Tests con mínimo mantenimiento ante evolución del sistema Robustos -> Setup + Teardown y aislados del ambiente
FILOSOFÍA • Escribir tests antes o después del código a probar
• Probar uno a uno o todo a la vez
• Probar desde afuera hacia adentro o desde adentro a afuera
• Probar comportamiento o estados
• Establecer condiciones de prueba por test o condiciones para todos
Escribir tests antes o después del código a probar Escribir primero el test ayuda a pensar la forma en que será utilizado el componente a desarrollar y a la asignación de responsabilidades a las clases, garantiza la testteabilidad, hace más naturla el código de acceso a los estados y a la verificación de resultados y contribuye a la valización de los requerimientos. Esta forma de trabajo es conocida como Test Driven Design (TDD).
Probar uno a uno o todo a la vez Desarrollo incremental -> test, código, test, código
Variante-> conjunto de métodos vacíos cohesivos desde el negocio son llenados de a uno
Probar desde afuera hacia adentro o desde adentro a afuera Ver figura siguiente
Probar comportamiento o estados Afuera -> adentro facilita la prueba de comportamiento. Más costoso por el uso de MockObjects, Test Spies, etc.
Establecer condiciones de prueba por test o condiciones para todos Testing tradicional-> varios tests con DB poblada de datos para todos ellos.
8
PRUEBAS DE SOFTWARE de tipo xUnit
Tendencia-> Fresh Fixture, MinimalFixture
PRINCIPIOS AL IGUAL QUE LOS CRITERIOS SON DISCUTIBLES Y SON MÁS ABSTRACTOS QUE LOS PATRONES. EXISTE LA MISMA RELACIÓN QUE EN EL DISEÑO CON LOS CRITERIOS DE BUEN DISEÑO Y LOS PATRONES DE DISEÑO.
Escriba el test primero
Diseñe para que haya testeabilidad
Utilice las interfaces públicas siempre que pueda, antes que accesos no visibles o recursos privados
Comunique el intento, para facilitar la lectura y mantenimiento
No modifique el SUT, para garantizar que se prueba lo desarrollado y lo que irá al ambiente productivo
Mantenga a los tests independientes para eliminar dependencias y facilitar el aislamiento de errores
Aisle al SUT para no depender del ambiente
Minimice el solapamiento de tests para facilitar el mantenimiento
Minimice el código no probado para evitar errores en pruebas funcionales y en ambiente productivo (Ej if prueba uso StubeObject)
Mantenga la lógica de prueba fuera del código productivo para evitar fallas en producción
Verifique una condición a la vez para facilitar el aislamiento de errores con sus fases de establecimiento de condiciones, ejecución, verificación de resultados y desarmado de condiciones. (Un assert por condición o agrupamiento en CustonAssert)
Pruebe incumbencias por sep arado para facilitar el aislamiento de errores y el
Asegure esfuerzo de tests en relación con el desarrollo para que sea sustentable
mantenimiento de los tests
9
PRUEBAS DE SOFTWARE de tipo xUnit
Estrategia Una estrategia está dada por un conjunto de decisiones que son difíciles de cambiar. Es por esto que es importante elaborarla con cuidado ya que en ella se basará nuestra planificación de las pruebas de nuestro sistema en desarrollo.
Entre las decisiones que hacen a la estrategia están:
Qué clase de test automatizar
Cuáles herramientas utilizar para la automatización
Cómo administrar las condiciones de prueba
Cómo asegurar que el sistema será eficientemente probado
QUÉ CLASE DE TEST AUTOMATIZAR
Figura extraída de [1]
10
PRUEBAS DE SOFTWARE de tipo xUnit
Figura extraída de [1]
CUÁLES HERRAMIENTAS UTILIZAR PARA LA AUTOMATIZACIÓN
Figura extraída de [1]
11
PRUEBAS DE SOFTWARE de tipo xUnit
CÓMO ADMINISTRAR LAS CONDICIONES DE PRUEBA Condiciones (test fixture): todo lo que se nesecita previamente para ejecutar los tests. Datos externos, objetos creados, etc. constituyen las condiciones para las pruebas. Tienen gran impacto sobre el tiempo de ejecución y la robustes de los tests.
Figura extraída de [1]
Frescas y transitorias: son creadas por cada test cuando son ejecutados.
Frescas y persistentes: funcionan en escenarios donde se prueban componentes acoplados por algún medio persistente. Necesitan código que se ocupe de desarmarlas (tear down).
Compartidas: funcionan en escenarios donde se busca optimizar la ejecución de tests cuando se usa la táctica de Fresca y Persistentes. Necesitan código que se ocupe de establecerlas y desarmarlas. Las distintas formas de implementación se muestran en la figura que sigue.
Figura extraída de [1]
12
PRUEBAS DE SOFTWARE de tipo xUnit
CÓMO ASEGURAR QUE EL SISTEMA SERÁ EFICIENTEMENTE PROBADO
Probar lo antes posible: mientras se diseña y desarrolla
Diseñar para testeabilidad: utilizando criterios de buen diseño
Pruebas estructuras de forma de contar con puntos de control y de observación.
Figura extraída de [1]
Utilizar una táctica de prueba para cada caso (patrones), por ejemplo:
Fake Object: para reemplazar componentes de los cuales se depende y mejorar repetibilidad o performance
TestStub: para controlar entradas indirectas
MockObject: para verificar salidas indirectas
SpyTest: para verificar comportamiento a través de salidas indirectas
TestDouble: para reemplazar subsistemas con los que se interactua y obtener entradas indirectas
13
PRUEBAS DE SOFTWARE de tipo xUnit
Fake Object
Figura extraída de [1]
Reemplazamos componentes para mejorar la performance del test. Ej. Pruebas por capas, donde las inferiores son reemplazadas por Fakes.
Test Stub y Mock Object
Figura extraída de [1]
Entradas indirectas usadas como punto de control, salidas indirectas usadas como punto de observación. Ej. Stub : Excepciones, Ej. Mock: ResultSet
14
PRUEBAS DE SOFTWARE de tipo xUnit
Pruebas por capas
Figura extraída de [1]
Ej. Prueba de una aplicación enterprise desde la capa de presentación, reemplazando al sistema (acceso al negocio, negocio, persistencia) por una FakeObject.
15
PRUEBAS DE SOFTWARE de tipo xUnit
xUnit DESCRIPCIÓN DE LAS PAUTAS INICIALES (REQUERIMIENTOS).
⇒ Pruebas de ejecución automática una vez construidas -> permite su ejecución continua
⇒ Pruebas repetitivas -> genera seguridad
⇒ Facil de construir y ejecutar por los desarrolladores -> permite mismo lenguaje de desarrollo y garantiza que serán construidas
⇒ Verifique resultados y los informe
⇒ Manipulación de casos de prueba como si fueran objetos
DISEÑO DE JUNIT 1. Encapsular en un objeto un test -> Command
public abstract class TestCase implements Test { … }
2. Cada test tendría un nombre para identificarlo cdo. falla por ejemplo
public abstract class TestCase implements Test { private final String fName;
public TestCase(String name) { fName= name; }
public abstract void run(); … }
3. Dónde escribir el código de seteo de condiciones, ejecución, prueba de resultados y destrucción de condiciones. Este es un algoritmo prefijado, luego utilizamos un TemplateMethod
public void run() { setUp(); runTest(); tearDown(); }
4. Necesitamos informar resultados
Recolectamos resultados public class TestResult extends Object { protected int fRunTests;
public TestResult() { fRunTests= 0; } }
modificamos el método run original para utilizarlo
16
PRUEBAS DE SOFTWARE de tipo xUnit
public void run(TestResult result) { result.startTest(this); setUp(); runTest(); tearDown(); }
public synchronized void startTest(Test test) { fRunTests++; }
public TestResult run() { TestResult result= createResult(); run(result); return result; }
protected TestResult createResult() { return new TestResult(); }
5. Distinguir entre fallas y errores
public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) { //1 result.addFailure(this, e); }
catch (Throwable e) { // 2 result.addError(this, e); } finally { tearDown(); } }
protected void assertTrue(boolean condition) { if (!condition) throw new AssertionFailedError(); }
public class AssertionFailedError extends Error { public AssertionFailedError () {} }
public synchronized void addError(Test test, Throwable t) { fErrors.addElement(new TestFailure(test, t)); }
public synchronized void addFailure(Test test, Throwable t) { fFailures.addElement(new TestFailure(test, t)); }
public class TestFailure extends Object { protected Test fFailedTest; protected Throwable fThrownException; }
6. Cómo relaciono a la clase TestCase con el código que escribimos para probar y
con la clase popietaria del código a probar ¿?
17
PRUEBAS DE SOFTWARE de tipo xUnit
Una posibilidad es utilizar un adapter, así tendríamos una clase adaptadora por cada tests con la consiguiente proliferación de clases.
class MiTestMetodo_1_claseA extends TestCase {
runTest(){ a.metodo_1();
} A a;
} En lugar de esto usaron clases anónimas
TestCase test= new Test("testMetodo_1 ") { protected void runTest() { A a = new A(); a.metodo_1(); } };
7. Ejecutar todos los test programados como métodos de una clase de prueba
Uncomposite es el patrón indicado
public static Test suite() { TestSuite suite= new TestSuite(); suite.addTest(new MoneyTest("testMoneyEquals")); suite.addTest(new MoneyTest("testSimpleAdd")); }
public static Test suite() { return new TestSuite(MoneyTest.class); }
18
PRUEBAS DE SOFTWARE de tipo xUnit
cd JUnit
«interface»Tets
+ run(TestResult) : void
TestCase
- name: String
+ assertEquals(boolean, boolean) : void+ fail() : void+ run() : TestResult+ run(TestResult) : void# runtest() : void# setUp() : void+ suite() : Test# tearDown() : void+ TestCase(String) : void
public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) { //1 result.addFailure(this, e); } catch (Throwable e) { // 2 result.addError(this, e); } finally { tearDown(); } }
protected void assert(boolean condition) { if (!condition) throw new AssertionFailedError(); }
TestResult
+ startTest(Test) : void
public synchronized void addError(Test test, Throwable t) { fErrors.addElement(new TestFailure(test, t)); } public synchronized void addFailure(Test test, Throwable t) { fFailures.addElement(new TestFailure(test, t)); }
AssertionFailedError
Error
TestFailure
Throwable
MiTest
# runtest() : void+ testAlgo() : void
1 - El adapter estandard requiere una clase por método !!!!!!!!protected void runTest () { testAlgo(); } . Demasiadas clases.
2 - En Java un adapter con clase anónima interna que redefine runTest()TestCase test= new MiTest("algo") { public void runTest() { testAlgo(); } }; test.run();
3 - Reflection (Plugeable Selector)TestCase test = new MiTest("testAlgo"); // busca por nombre test.run();....protected void runTest() throws Throwable { Method runMethod= null; try { runMethod= getClass().getMethod(fName, new Class[0]); } catch (NoSuchMethodException e) { assert("Method \""+fName+"\" not found", false); } try { runMethod.invoke(this, new Class[0]); // en este caso no tenemos parámetros, luego es un array vacío } // catch InvocationTargetException and Il legalAccessException }
Command
Adapter
TestSuite
+ run(TestResult) : void
public void run(TestResult result) { for (Enumeration e= fTests.elements(); e.hasMoreElements(); ) { Test test= (Test)e.nextElement(); test.run(result); } }
Assert
+ assertEquals(boolean, boolean) : void+ fail() : void
Composite
Template MethodFactory Method
Plugeable Selector
19
PRUEBAS DE SOFTWARE de tipo xUnit
Condiciones de prueba
CONDICIONES TRANSITORIAS
Formas de establecimiento ⇒ Inline
⇒ Delegada
⇒ Implícita
Son establecidas en y para cada ejecución de cada uno de los tests.
CONDICIONES PERSISTENTES Frescas: Son aquellas establecidas y que persisten más allá de la ejecución del test, por ejemplo datos a una DB, variables de clases, objetos statefull, etc. Esto es así porque al ser frescas se crean y destruyen por cada test.
Desventajas • Tests lentos
Soluciones Reemplazar persistencia en file system y/o DB por memoria, posible uso de FakeObject.
Test encadenados
Figura extraída de [1]
Ejemplo de tests encadanados: pruebas de ABM (alta, baja, modificación)
20
PRUEBAS DE SOFTWARE de tipo xUnit
cd PruebasEncadenadas
TestDataMapperABM
ProyectoDataMapper
testAlta
testModificar
testBaja
TestRunner
TestCase
testConfirmaBorrado
«call»
assert 1
«call»assert 2
«call»
assert 3
«instantiate»
«instantiate»
«instantiate»«call»
assert 4
21
PRUEBAS DE SOFTWARE de tipo xUnit
Verificación de resultados VERIFICACIONES BÁSICAS Comparación de Resultados Esperados contra Resultados Obtenidos a partir de Asserts
assertEquals(3, value); donde 3 es el valor esperado y value es el obtenido !!!, value es de tipo primitivo.
Comparación de objetos de tipo ValueObjects Necesita de método de comparación (Object.equals()) adecuadamente redefinido. Debe cumplir con las propiedades de reflexiva, simétrica y transitiva.
Test de constructores
A partir de la verid¡ficación de sus atributos accedidos con get o agregar método isValido() si no se desea dar acceso a los atributos.
Test de get y Set Deberían escribirse solo si además de dar acceso se realiza algo más- Ej. Cálculos, o cualquier indirección. Utilizo los gets asociados o algún acceso indirecto que utilice los atributos seteados.
Test de interfaces
Requiere de un test que permita probar todas las posibles implementaciones. Ej. Iterator.
22
PRUEBAS DE SOFTWARE de tipo xUnit
cd TestInterfaces
AssertJUnit::TestCase
- name: String
+ assertEquals(boolean, boolean) : void+ fail() : void+ run() : TestResult+ run(TestResult) : void# runtest() : void# setUp() : void+ suite() : Test# tearDown() : void+ TestCase(String) : void
IteratorTest
- noMoreElementsIterator: Iterator
+ makeNoMoreElementsIterator() : Iterator# setUp() : void
ListIteratorTest
+ makeNoMoreElementsIterator() : Iterator
SetIteratorTest
+ makeNoMoreElementsIterator() : Iterator
public abstract class IteratorTest extends TestCase {private Iterator noMoreElementsIterator;protected abstract Iterator makeNoMoreElementsIterator();protected void setUp() {noMoreElementsIterator = makeNoMoreElementsIterator();}public void testHasNextNoMoreElements() {assertFalse(noMoreElementsIterator.hasNext());}public void testNextNoMoreElements() {try {noMoreElementsIterator.next();fail("No exception with no elements remaining!");}catch (NoSuchElementException expected) {}}public void testRemoveNoMoreElements() {try {noMoreElementsIterator.remove();fail("No exception with no elements remaining!");}catch (Il legalStateException expected) {}}}
Test de JavaBeans
Consistirían en un conjunto de pruebas de gets y sets. Es más útil probar estado interno a través de métodos de validación tipo isValid().
Test de Excepciones
Requiere la prueba de que se dispare la correcta.
public void testConstructorNull() throws Exception { try {
Fraction oneOverZero = new Fraction(1, 0); fail("Intentó crear la fracción 1/0, este valor es indefinido ¡!"); // si no
se dispara avisa } catch (IllegalArgumentException expected) {
assertEquals("denominator", expected.getMessage()); }
}
23
PRUEBAS DE SOFTWARE de tipo xUnit
Test de Colecciones Las colecciones se comparan solas, no recorra todos los elementos comparandolos. Tenga en cuenta que:
Investigar http://www.GBase.sourceforce.com , hay APIs para esto.
Test de Objetos que crean otros objetos
Investigar http://www.easymock.org
Cómo probar un Singleto¿?
Cómo probar un Factory¿?
24
PRUEBAS DE SOFTWARE de tipo xUnit
Verificación de comportamiento
Figura extraída de [1]
Realizamos chequeos (asserts) sobre puntos de observación insertados entre el SUT y el DOC a efectos de monitorear su interacción y medir los resultados esperados.
Verificación de estado
Figura extraída de [1]
Chequeos (asserts) después de ejecutarlo, directamente sobre los puntos de observación, sobre datos que los métodos del SUT devuelven.
25
PRUEBAS DE SOFTWARE de tipo xUnit
Las Pruebas como criterio de diseño EJEMPLO Capa de persistencia de una aplicació entreprise
cd Persistencia
AbstractMapper
MapperConcreto
«interface»ProyectosFinder
ProyectoJdbcQueryBuilder
JdbcQueryBuilder
+ createPreparedStatementParameters(domainParameters :List, statementName :String) : List+ getSqlString(statementName :String) : String+ getSupportedStatements() : Map+ loadStatements(losStatements :Map) : void+ supportsStatement(statementName :String) : boolean
«interface»PreparedStatementBuilder
+ getPreparedStatementData(domainParameters :List, statementName :String) : PreparedStatementData
PreparedStatementData
JdbcQueryExecuter
+ executeQuery(qc :QueryCallback, preparedStatementData :PreparedStatementData) : Object
«interface»QueryCallback
+ doInQuery(result :int) : Object
Las clases en verde son las correspondientes al diseño original. Las restantes aparecen aplicando criterios de diseño basados en las pruebas.
26
PRUEBAS DE SOFTWARE de tipo xUnit
Utilizando plugins TESTDOUBLE
⇒ TestStub
⇒ MockObject
⇒ TestSpy
⇒ FakeObject
⇒ TestSubclass
Creación y configuración Test Stubs y MockObjects necesitan la configuración de los valores a devolver y los valores esperados.
Fake Object y Spy Object no necesitan configuración.
Figura extraída de [1]
Instalación • Por inyección de dependencia
• Utilizando un Service Locator
27
PRUEBAS DE SOFTWARE de tipo xUnit
Figura extraída de [1]
Instalación utilizando un Service Locator
cd Serv iceLocator
Registry
MapperConcreto
SpyProyectosDataMapperTestUnitOfWork
UnitOfWork
Proyecto
<?xml version="1.0" encoding="ISO-8859-1"?><configuracion><modofuncionamiento name="modoprueba" /><modoproduccion><mappers><mapper name="Proyecto" class="capaPersistencia.codigo.impl.ProyectosDataMapper" findStatement="SELECT * FROM Proyecto WHERE IdProyecto = ?" ......</mappers></modoproduccion><modoprueba><mappers><mapper name="ProyectoSpy" class="capaPersistencia.codigo.impl.SpyProyectosDataMapper" findStatement="SELECT * .......
</modoprueba></configuracion>
«access»find, create
obtenido, (nuevo, modificado, borrado)
esperado, adaptar
Ej. Esquema utilizado en los ejemplos
28
PRUEBAS DE SOFTWARE de tipo xUnit
MockObject Ejemplo
Capa de persistencia de una aplicación enterprise
Figura extraída de [1]
Dinámica del test utilizando MockObject
cd PruebasResultSet
DataMapperProyectoTest
+ doLoad(ResultSet, Integer) : DomainObject
AbstractMapper
MapperConcreto
MockSingleRowResultSet
ProyectoTestResultSetOjectMapperProyecto
+ testNewProyecto() : void+ setUp() : void+ tearDown() : void
UnitOfWork
Registry
«instantiate»
«instantiate»
obtenido
«instantiate»
esperado
«instantiate»
setUp/tearDown
setUp/tearDown
Ejemplo: Prueba de la conversión de datos, atributos de objetos a campos de ResultSet
29
PRUEBAS DE SOFTWARE de tipo xUnit
Subclases
Figura extraída de [1]
Dinámica del test utilizando clases derivadas del SUT
cd PruebasStatements
AbstractMapper
MapperConcreto
TestPreparedStatementMapperProyecto
+ testUpdateStatement() : void+ testFindPreparedStatement() : void
ProyectoJdbcQueryBuilder
ListToFind DataMapperProyectoTest_2
+ getAtributosAsList(DomainObject) : List
Proyecto
ListToUpdate
UnitOfWork Registry
obtenido
«instantiate»
esperado
«instantiate»
esperado
parametersAsToStatement
obtenido
createPreparedStatementParameters domainParameters
domainObject
«instantiate»
esperado
«access»
«instantiate»
getAtributosAsList
setUp / tearDown
setUp / tearDown
Ejemplo: Prueba de los strings asociados a los comandos de SQL
30
PRUEBAS DE SOFTWARE de tipo xUnit
SpyObject
Figura extraída de [1]
Dinámica del test utilizando SpyObject
31
PRUEBAS DE SOFTWARE de tipo xUnit
cd PruebasSpy
Si no se piden proveedores ->getCountInvocacionesProveedores = 0Al hacer getProveedores -> getCountInvocacionesProveedores = 1
Al no salvarlo, si igual descripcion -> viene del cahe
SpyProyectosDataMapper
- countInvocacionesProveedoresLazy: int- idInvocacionesProveedoresLazy: Vector
+ loadProveedores(int) : List+ getCountInvocacionesProveedores() : int+ getIdInvocacionesProvedores() : Vector
AbstractMapper
MapperConcretoRegistryUnitOfWork
TestDataMapperProyecto
+ testIdentityMap() : void+ testLazyLoadProveedores() : void+ testLazyLoadCliente() : void
Proyecto
Proyecto
«access»
find
setUp / tearDown setup / tearDown
esperado , setDescripcion
obtenido, getDescripcion
Ejemplo: Prueba del comportamiento del Identity Map (cache)
32
PRUEBAS DE SOFTWARE de tipo xUnit
cd PruebasUoW
TestUnitOfWork
+ testTransaccion() : void
RegistryUnitOfWork
SpyProyectosDataMapper
- countInvocacionesProveedoresLazy: int- idInvocacionesProveedoresLazy: Vector- objetoEstadoPersistencia: Map<Integer, String>
+ loadProveedores(int) : List+ getCountInvocacionesProveedores() : int+ getIdInvocacionesProvedores() : Vector+ insert(DomainObject) : void+ update(DomainObject) : void+ delete(DomainObject) : void
AbstractMapper
MapperConcreto
Proyecto
Proyecto
Proyecto
«access»find, create
esperado, save
esperado, adaptar
esperado, borrar
obtenido, (nuevo, modificado, borrado)
Ejemplo: Prueba de la dinámica de la UnitOfWork.
33
PRUEBAS DE SOFTWARE de tipo xUnit
FakeObject
Figura extraída de [1]
Dinámica del test utilizando Fake Object
cd PruebasFake
FakeAdminCliente
- clientes: Map<String, ClienteDTO>
+ init() : void
AdminClienteFactoryPresentacion
«interface»IAdminCliente
BrowserWatir
«instantiate»
Ejemplo: Prueba de presentación y comunicación con el cliente.
34
PRUEBAS DE SOFTWARE de tipo xUnit
Pruebas con bases de datos Ver DBUnit
35
PRUEBAS DE SOFTWARE de tipo xUnit
Organización de los tests de un proyecto ESTRATEGIAS
1. TestClass por clase
2. TestClass por funcionalidad
3. TestClass por condición
TestClass por clase
Figura extraída de [1]
36
PRUEBAS DE SOFTWARE de tipo xUnit
TestClass por funcionalidad
Figura extraída de [1]
TestClass por condicón de prueba
37
PRUEBAS DE SOFTWARE de tipo xUnit
Figura extraída de [1]
38
PRUEBAS DE SOFTWARE de tipo xUnit
Herramientas
PRUEBAS DE LAS DISTINTAS CAPAS
cd ICPruebas
Pruebas de DBHerramientas: DBUnit
Prueba de Acceso al NegocioHerramientas: JUnit, FakeObject
Prueba de PresentaciónHerramientas: JUnit, ServletUnit
Prueba de Reglas de NegocioHerramientas: JUnit
Prueba con y sin la DBHerramientas: JUnit, MockObject, SpyObject
Web Browser
Web Server
Negocio PersistenciaApp Server
DB
Prueba de ClienteHerramientas: TestWatir
FRAMEWORKS
JUnit Pruebas genéricas
Escrito en Java
Sitio: http://www.junit.org/
ServletUnit Pruebas de capa de presentación
Escrito en Java
Emula en forma simplificada un Java web sever
Sitio: http://httpunit.sourceforge.net/doc/servletunit-intro.html
HttpUnit Pruebas de conectividad y cliente
Emula un web browser
Necesita un web server para funcionar
Sitio: http://httpunit.sourceforge.net/
39
PRUEBAS DE SOFTWARE de tipo xUnit
Watir Pruebas de interacción entre el web browser y el web server
Instancia un web browser (Windows Internet Browser)
Necesita un web server para funcionar
Sitio: http://wiki.openqa.org/display/WTR/Tutorial
40
PRUEBAS DE SOFTWARE de tipo xUnit
Referencias
1. xUnit Test Patterns: Refactoring Test Code, Gerard Meszaros, Addison-Wesley, 2007.
2. JUnit Recipes - Practical Methods for Programmer Testing, J.B. Rainsberger
with contributions by Scott Stirling, Manning, 2005.
3. The Craft of Software Testing,Brian Marick , PH, 1995.
4. The Art of Software Testing (2nd edition), Glenford J. Myers, John Wiley & Sons, Inc, 2004.
5. Code Complete (2nd edition), Steve McConnell, Microsoft Press, 1993.
6. A Practical Guide to Testing Object – Oriented Software, John McGregor, David Sykes, Addison Wesley, 2001.
7. Systematic Software Testing, Rick D. Craig, Stefan P. Jaskiel, Artech House
Publishers, 2002.
41