• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Proves de Software (en Java amb JUnit)
 

Proves de Software (en Java amb JUnit)

on

  • 1,388 views

Apunts de proves unitàries en Java usant JUnit.

Apunts de proves unitàries en Java usant JUnit.

Statistics

Views

Total Views
1,388
Views on SlideShare
1,383
Embed Views
5

Actions

Likes
0
Downloads
3
Comments
0

1 Embed 5

http://www.slideshare.net 5

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Proves de Software (en Java amb JUnit) Proves de Software (en Java amb JUnit) Presentation Transcript

    • Proves de software (en Java amb JUnit) Juan Manuel Gimeno Illa jmgimeno@diei.udl.cat
    • Índex ● Què i per què dels tests unitaris ● Com? ● El framework JUnit (3.x) ● Proves en aïllament ● Tipus de proves dins del cicle de vida 2
    • Necessitat dels tests unitaris ● Quan programem necessitem saber si el que hem construït realment funciona ● Per tant el que hem de fer és executar els nostres “tests d'acceptació” ● Normalment ho fem però de forma informal: ● No són automàtics (provem a mà) ● No els repetim (problemes de regressió) ● No són focalitzats (provem moltes coses de cop) 3
    • Definició de test unitari ● Un test unitari comprova que un mètode ● accepta un rang de valor esperats ● i que retorna els valors esperats per cada valor provat. ● Per tant el que volem és provar el mètode a través de la seva especificació: ● Si li passem el valor x retornarà el valor correcte y? ● Si li passem el valor z llençarà l'excepció prefixada? 4
    • Fent tests manualment ● Suposem que volem testejar la següent classe: public class Calculator { public double add(double number 1, double number2) { return number1 + number 2; } } 5
    • Fent tests manualment ● Com podem provar aquesta classe? ● Fem un programa que demani dos nombres i comprovem que el resultat és correcte? ● Quants nombres entrarem? ● Si trobem un error i modifiquem el programa, recordarem els que haviem provat abans? ● La idea és fer un programa que faci això automàticament de manera que es pugui repetir tantes vegaden com calgui!! 6
    • Programant una classe de prova public class Test Calculator { public static void main (String[] args) { Calculator calculator = new Calculator(); double result = calculator.add(10, 50); If (result != 60) { System.out.println(“Bad result: “ + result); } } } ● S'ha d'estar pendent de la sortida per si hi ha errors ● A més la forma convencional d'assenyalar errors en Java són les excepcions 7
    • Millorant la classe de prova Cada prova està public class TestCalculator { implementada en private int nbErrors = 0; un mètode public void testAdd() { Calculator calculator = new Calculator(); double result = calculator.add(10, 50); if (result != 60) { throw new RuntimeException(“Bad result: “ + result); } } public static void main(String[] args) { TestCalculator test = new TestCalculator(); try { test.testAdd(); Fàcil afegir } catch (Throwable e) { noves proves test.nbErrors++; e.printStackTrace(); } if (test.nbErrors > 0) { throw new RuntimeException(“There were “ + test.nbErrors + “ error(s)”); } } 8 }
    • Frameworks de proves unitàries ● Un framework per p.u. ha de seguir un conjunt de “bones pràctiques” ● Cada test ha d'executar-se de forma independent a tots els altres ● Els errors s'han de detectar i mostrar test a test ● Ha de ser fàcil poder seleccionar quins tests s'executaran ● El framework JUnit proveeix de totes aquestes facilitats (i més) 9
    • El Framework JUnit ● Proveeix diversos front-ends per mostrar els resultats dels tests (línia de comanda, awt, swing, …) ● Classloaders diferents per a cada test per evitar “efectes laterals” ● Mètodes estàndard per inicialitzar i alliberar recursos (setUp and tearDown) ● Varietat d'assercions per a comprovar els resultats de les proves ● Integració amb IDEs, ant, maven, .... 10
    • La prova usant JUnit import junit.framework.TestCase public class TestCalculator extends TestCase { public void testAdd() { Calculator calculator = new Calculator(); double result = calculator.add(10, 50); assertEquals(60, result, 0); } } ● javac -cp ../junit3.8.2/junit.jar *.java ● java -cp .:../junit3.8.2/junit.jar ↳junit.swingui.TestRunner TestCalculator 11
    • Nomenclatura de JUnit ● Test case: Classe que estén TestCase i que conté una o més proves representades pels mètodes testXXX. S'usa per agrupar proves que exerciten comportaments comuns. ● TestSuite: Agrupació de proves que estan relacionades. ● TestRunner: Llençador de grups de proves. Es prové de la classe BaseTestRunner. TestCase + Test Suite + BaseTestRunner = TestResult 12
    • Interfícies i classes de JUnit ● Assert: Conté mètodes que no fan res si la comprovació és exitosa, i llencen una excepció quan falla. ● TestResult: Recolecta els errors i fallides que es produeixen a l'executar les proves. ● Test: Un test es pot executar i rep un TestResult. ● TestListener: Rep notificacions dels events que succeeixen durant una prova (p.e. la prova comença o acaba, s'ha produït un error, etc.) 13
    • Interfícies i classes de JUnit (2) ● TestCase: Defineix un entorn (o fixture) que pot usar-se per executar múltiples tests. ● TestSuite: executa una col·lecció de tests, que poden incloure també altres TestSuites. ● BaseTestRunner: superclasse de tots els llençadors de tests. 14
    • La interfície Test ● La interfície que implementen tant la classe TestCase com la TestSuite s'anomena Test i conté el següent: package junit.framework; public interface Test { public abstract int countTestCases(); public abstract void run(TestResult result); } 15
    • Diagrama de classes 16
    • Creant una TestSuite ● Una TestSuite serveix per a que un TestRunner executi conjuntament diversos Tests relacionats ● Però no es vol complicar l'execució de TestCases individualment ● Solució: TestRunner crea una TestSuite de forma automàtica a partir d'un TestCase ● (Més endavant veurem que Test, TestSuite i TestCase són un exemple de patró Composite) 17
    • La TestSuite “automàtica” ● Escaneja la classe de test per mètodes que comencin per “test” ● Crea una instància de TestCase per a cadascun d'aquests mètodes ● El nom del mètode és passat al constructor per identificar cada instància ● Aquest constructor amb nom era obligatori abans de JUnit 3.8. Ara només cal si usat explícitament. ● La TestSuite es crea dins del mètode públic i estàtic suite() 18
    • suite() per TestCalculator ● El mètode estatic que es crearia és equivalent a: public static Test suite() { return new TestSuite(TestCalculator.class); } ● Que és equivalent a: public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new TestCalculator(“testAdd”)); return suite; } 19
    • La típica classe TestAll import junit.framework.Test; import junit.framework.TestSuite; public class TestAll { public static Test suite() { TestSuite suite = new TestSuite(“Tots els tests”); suite.addTestSuite(TestCalculator.class); suite.addTestSuite(TestGenerator.suite()); return suite; } } 20
    • Recollint resultats amb TestResult ● Una instància de TestResult recull informació sobre si els tests són exitosos o fallen. ● P.e. si la línia assertEquals(60, result, 0) falla, es crea una instància de TestFailure i es guarda a TestResult ● TestRunner usa TestResult per informar de com ha anat l'execució dels tests: ● si no hi ha cap TestFailure a TestResult, green bar ● si n'hi ha, s'indica el nombre d'errors i es mostra un volcat de la traça de la pila 21
    • Fallides i Errors ● JUnit distingeix entre: ● fallida: es 'normal' que es produeixin per exemple degut a canvis en el codi. ● error: condició no esperada per un test i que no s'hauria de produir mai. ● Quan es produeix una fallida: ● s'arregla el codi per arreglar la situació ● Quan es produeix un error: ● comprovar l'entorn (configuració, xarxa, bbdd, ...) ● comprovar el test ● comprovar el codi 22
    • Fallida 23
    • Error 24
    • Observant execució amb TestListener ● La interfície TestListener permet definir objectes que accedeixin al TestResult per a informar sobre el resultat d'un test. ● Per exemple, els diferents TestRunners implementen aquesta interfície ● Es poden enregistrar tants TestsListeners com es vulgui per a fer el que es necessiti amb el TestResult. 25
    • Interfície TestListener public interface TestListener { // Cridat per notificar que s'ha produït un error void addError(Test test, Throwable t); // Cridat per notificar que s'ha produït una fallida void addFailure(Test test, AssertionFailedError e); // Cridat per notificar la fi d'execució d'un test void endTest(Test test); // Cridat per notificar l'inici d'execució d'un test void startTest(Test test); } 26
    • Treballant amb TestCase ● Recordem que TestRunner executa una TestSuite que conté un o més TestCases (o d'altres TestSuites) ● El framework conté varis TestRunners ● El framework crea una TestSuite per defecte ● Per tant, la única classe que és absolutament necessari programar és TestCase 27
    • Manegant recursos dins d'un TestCase ● Alguns tests requereixen recursos per funcionar (p.e. una connexió amb la BBDD) ● Varis tests d'un mateix TestCase poden necessitar aquests mateixos recursos ● Replicar el codi de creació i configuració d'aquests recursos en cada test no té sentit ● Fixture (accessori): el conjunt de recursos (o dades) comuns que es necessiten per executar un o més tests. 28
    • Execució dels mètodes testXXX ● Una fixture és creada i destruïda pels mètodes setUp i tearDown de TestCase. ● El TestCase crida de forma automática setUp (tearDown) abans (després) d'executar cada mètode de test. ● Una de les raons de posar varis mètodes de test a un TestCase és compartir la fixture. 29
    • El supertipus Assert ● Classe d'utilitat que conté mètodes per avaluar condicions ● Tots ells llencen AssertionFailedError quan fallen i tenen vàries versions (p.e. AssertEquals en té 20) ● Mirant el javadoc un veuen 38 mètodes però en el fons “només” són 8 mètodes: assertTrue assertNull assertFalse assertSame assertEquals assertNotSame assertNotNull fail 30
    • Els altres mètodes de TestCase public abstract class TestCase extends Assert implements Test { public TestCase() {...} public TestCase(String name) {...} public int countTestCases() {...} protected TestResult createTestResult() {...} public TestResult run() {...} public void run(TestResult result) {...} public void runBare() throws Throwable {...} protected void runTest() throws Throwable {...} protected void setUp() throws Exception {...} protected void tearDown() throws Exception {...} public String toString() {...} public String getName() {...} public String setName(String name) {...} } 31
    • Regla d'or de l'escriptura de tests ● Cada test unitari ha d'executar-se de manera independent de tots els altres tests unitaris ● han de poder executar-se en qualsevol ordre ● sense dependre d'efectes laterals produïts pels tests previs ● Problemes que produeixen tests dependents: ● portabilitat (ja que JUnit usa reflexió) ● mantenibilitat ● llegibilitat 32
    • Recordem TestCalculator ● El que farem es tracejar l'execució de la classe TestCalculator, mitjançant varis diagrames de seqüència ● Recordem el codi de la classe: import junit.framework.TestCase public class TestCalculator extends TestCase { public void testAdd() { Calculator calculator = new Calculator(); double result = calculator.add(10, 50); assertEquals(60, result, 0); } } 33
    • Tracejant TestCalculator ● Quan s'executa java junit.swingui.TestRunner TestCalculator ● El framework JUnit fa el següent: ● crea una TestSuite ● crea un TestResult ● executa els mètodes de test (en aquest cas testAdd) 34
    • Creant una TestSuite (explícitament) ● Si TestCase conté un mètode suite explícit: public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new TestCalculator(“testAdd”)); return suite; } 35
    • Creant una TestSuite (implícitament) ● Si no hi ha mètode suite, via instrospecció, es crea la TestSuite buscant mètodes que contenen mètodes testXXX. 36
    • Creant un TestResult 37
    • Creant un TestResult (cont.) (1)TestRunner instancia el TestResult (2)TestRunner s'enregistra com observador del TestResult (3)TestRunner crida al mètode run de la TestSuite el qual crida al mètode run del TestCase i aquest crida al mètode run del TestResult (4)El TestResult notifica que comença a executar un test (5)TestResult executa el test amb el mètode runBare 38
    • Executant els mètodes de test 39
    • Executant els mètodes de test (cont.) (1)runBare és el responsable de cridar a setUp, al mètode de test (testAdd) i a tearDown (2)en cas de produir-se un una fallida (durant l'execució de qualsevol d'aquests tres mètodes), aquesta es notifica a TestRunner. (3)en cas d'error inesperat, també es notifica (4)finalment, es notifica que s'ha acabat l'execució del test. 40
    • Exemple de classe de proves public class OrderStateTester extends TestCase { private static final String TALISKER = "Talisker"; private static final String HIGHLAND_PARK = "Highland Park"; private Warehouse warehouse = new WarehouseImpl(); protected void setUp() throws Exception { warehouse.add(TALISKER, 50); warehouse.add(HIGHLAND_PARK, 25); } public void testOrderIsFilledIfEnoughInWarehouse() { Order order = new Order(TALISKER, 50); order.fill(warehouse); assertTrue(order.isFilled()); assertEquals(0, warehouse.getInventory(TALISKER)); } public void testOrderDoesNotRemoveIfNotEnough() { Order order = new Order(TALISKER, 51); order.fill(warehouse); assertFalse(order.isFilled()); assertEquals(50, warehouse.getInventory(TALISKER)); } 41 }
    • Estructura típica dels tests ● Un test sol tenir quatre fases: ● inicialització: parcialment es fa a setUp (warehouse) i a cada test (order) ● execució: es fa que l'objecte invoqui el mètode que es vol provar (order.fill) ● comprovació: es comprova que el resultat del mètode és l'esperat (asserts) ● neteja: s'alliberen recursos (en aquest cas es fa de forma implícita i ho farà el recol·lector de deixalles) 42
    • Nomenclatura ● System Under Test (SUT): objecte que s'està provant. En el nostre cas es tracta de l'objecte order ● Col·laborador(s): objectes que es necessiten per tal de poder provar el SUT. En el nostre cas es tracta de warehouse ● ho necessitem doncs order.fill crida a mètodes de warehouse ● també ho necessitem per a verificar que order.fill canvia correctament l'estat de warehouse 43
    • Verificació de l'estat ● És una de les formes de verificar que l'execució ha estat la correcta. ● Consisteix en verificar que tant l'estat del SUT com dels objectes col·laboradors són els correctes després d'executar el mètode que es vol provar. ● (Posteriorment veurem que els mocks permeten un altre estil de verificació). 44
    • Proves en aïllament ● Moltes vegades el codi que volem testejar depèn d'altres classes que depenen de l'entorn: ● usen JDBC per accedir a una base de dades ● utilitzen els serveis d'un contenidor J2EE ● accedeixen al sistema de fitxers ● connecten amb altres recursos via HTTP, SOA, ... ● Per “simular” aquests serveis externs podem usarem TestDoubles (objectes substitutius) 45
    • Tipus de TestDoubles ● Hi ha varis tipus de substituts: ● Dummy objects que només es passen per satisfer paràmetres ● Fake objects que tenen implementacions no vàlides per producció ● Stubs que tenen respostes prefixades a les crides que es fan al test ● Mocks que tenen expectatives pre-programades que formen una especificació de les crides que esperen rebre durant el test 46
    • Stubs/Fakes ● Com des del punt de vista del test es comporten igual, els analitzarem conjuntament (i farem servir només la denominació Stub) ● La intenció és substituir un comportament complex amb un de més simple que permeti el testeig independent de part del codi. ● no replicant tota la funcionalitat ● amb una implementació simple no vàlida per producció (p.e. bbdd en memòria) ● respostes prefixades 47
    • Utilitat dels stubs ● Els stubs donen confiança en el sistema sota proves ja que aquest no es modifica ● Quan pot ser útil usar stubs: ● no es pot modificar un sistema existent perquè és molt complex i fràgil ● per proves “gruixudes” com integració de diversos subsistemes 48
    • Exemple de Stub public interface MailService { public void send (Message msg); } public class MailServiceStub implements MailService { private List<Message> messages = new ArrayList<Message>(); public void send (Message msg) { messages.add(msg); } Només implementem public int numberSent() { la funcionalitat que return messages.size(); necessitem } } class OrderStateTester .... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Comprovem que MailServiceStub mailer = new MailServiceStub(); s'ha enviat el order.setMailer(mailer); correu order.fill(warehouse); assertEquals(1, mailer.numberSent()); } } 49
    • Inconvenients dels stubs ● Els stubs poden ser difícils d'implementar ja que s'ha de replicar la lògica de funcionament del sistema substituït ● Poden ser difícils de mantenir per la seva complexitat ● Un stub no s'adiu amb unit testing de “grau fi” ● Cada situació (element a substituir per un stub) necessita d'una estratègia particular. 50
    • Mocks ● Són objectes substitutius que es diferencien dels anteriors en la forma que es verifiquen: ● stubs/fakes: verificació d'estat, és a dir, al final del test comprovem que l'estat de l'objecte ● mocks: verificació de comportament ● Existeixen biblioteques addicionals que simplifiquen la seva creació: jMocK, EasyMock, Mockito, etc. 51
    • L'exemple usant jMock1 public class OrderInteractionTester extends MockObjectTestCase { private static final String TALISKER = "Talisker"; public void testFillingRemovesInventoryIfInStock() { //setup - data Order order = new Order(TALISKER, 50); Mock warehouseMock = new Mock(Warehouse.class); //setup - expectations warehouseMock.expects(once()).method("hasInventory") .with(eq(TALISKER),eq(50)) .will(returnValue(true)); warehouseMock.expects(once()).method("remove") .with(eq(TALISKER), eq(50)) .after("hasInventory"); //exercise order.fill((Warehouse) warehouseMock.proxy()); //verify warehouseMock.verify(); assertTrue(order.isFilled()); 52 } .....
    • L'exemple usant jMock1 (cont.) public void testFillingDoesNotRemoveIfNotEnoughInStock(){ Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); assertFalse(order.isFilled()); } ..... ● Crear el mock usant el mètode mock fa que no calgui verificar-lo explícitament al final del test ● Posem withAnyArguments doncs ja hem verificat abans que la crida es fa amb els arguments correctes i així el test és més robust 53
    • L'exemple usant jMock2 public class OrderInteractionTester extends MockObjectTestCase { private static final String TALISKER = "Talisker"; public void testFillingRemovesInventoryIfInStock() { //setup - data Order order = new Order(TALISKER, 50); Warehouse warehouseMock = mock(Warehouse.class); final Sequence seq = sequence(“seq”); //setup – expectations checking(new Expectations() {{ oneOf(warehouseMock).hasInventory(50); inSequence(seq); will(returnValue(true)); oneOf(warehouseMock).remove(50); inSequence(seq); }}); //exercise order.fill(warehouseMock); //verify assertTrue(order.isFilled()); } ..... 54
    • L'exemple usant jMock2 (cont.) public void testFillingDoesNotRemoveIfNotEnoughInStock(){ Order order = new Order(TALISKER, 51); Warehouse warehouseMock = mock(Warehouse.class); checking(new Expectations() {{ oneOf(warehouseMock).hasInventory(with(any(int.class))); will(returnValue(false)); }}); order.fill(warehouseMock); assertFalse(order.isFilled()); } ..... ● jMock2 requereix de Java 5 ● Requereix també les extensions Hamcrest de JUnit ● Un dels objectius és més seguretat dels tipus 55
    • L'exemple usant EasyMock public class OrderEasyTester extends TestCase { private static final String TALISKER = "Talisker"; private MockControl warehouseControl; private Warehouse warehouseMock; public void setUp() { warehouseControl = MockControl.createControl(Warehouse.class); warehouseMock = (Warehouse) warehouseControl.getMock(); } public void testFillingRemovesInventoryIfInStock() { //setup - data Order order = new Order(TALISKER, 50); //setup - expectations warehouseMock.hasInventory(TALISKER, 50); warehouseControl.setReturnValue(true); warehouseMock.remove(TALISKER, 50); warehouseControl.replay(); //exercise order.fill(warehouseMock); //verify warehouseControl.verify(); 56 assertTrue(order.isFilled()); }
    • L'exemple usant EasyMock (cont.) public void testFillingDoesNotRemoveIfNotEnoughInStock() { Order order = new Order(TALISKER, 51); warehouseMock.hasInventory(TALISKER, 51); warehouseControl.setReturnValue(false); warehouseControl.replay(); order.fill((Warehouse) warehouseMock); assertFalse(order.isFilled()); warehouseControl.verify(); } ● Fa servir la metàfora de la “gravadora” ● el mock segueix la interfície del col·laborador que volem substituir ● el control permet indicar coses com el valor de retorn ● una vegada indicat replay() sobre el control, l'objecte mock es comportarà de la forma gravada 57
    • Diferències amb les proves clàssiques ● La fase d'inicialització: ● la inicialització de les dades és igual però l'únic objecte “normal” que es crea és el SUT ja que els col·laboradors són mocks. ● la segona part crea expectatives en el mocks, que són els mètodes que s'han de cridar quan es posa en marxa la funcionalitat en el SUT ● La fase de verificació també té dues parts ● en una es comprova l'estat del SUT (com abans) ● es verifica que les expectatives sobre els mocks s'han complert 58
    • Verificació del comportament ● Fixeu-vos que sobre el mock no fem cap verificació del seu estat. ● La única cosa que es comprova és si el SUT ha invocat els mètodes adequats. ● És a dir, el que hem verificat és què s'hagi dut a terme la col·laboració adequada entre ells. ● Fixeu-vos que podem fer proves amb col·laboradors no implementats i que els mocks ens proporciones especificacions del comportament que han de tenir. 59
    • Comparant Stubs i Mocks public interface MailService { public void send (Message msg); } public class MailServiceStub implements MailService { private List<Message> messages = new ArrayList<Message>(); public void send (Message msg) { messages.add(msg); } public int numberSent() { return messages.size(); } } class OrderStateTester extends TestCase { public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); } } 60
    • Comparant Stubs i Mocks (cont.) class OrderInteractionTester extends MockObjectTestCase { public void testOrderSendsMailIfUnfilled() { Warehouse warehouseMock = mock(Warehouse.class); MailService mailerMock = mock(MailService.class); Order order = new Order(TALISKER, 51); order.setMailer(mailerMock); checking(new Expectations() {{ oneOf(mailerMock).send(); oneOf(warehouseMock).hasInventory(with(any(int.class))); will(returnValue(false); }}); order.fill(warehouseMock); } } 61
    • Millors pràctiques: què provar? ● Cal focalitzar-se en les coses que es poden trencar ● Els tests han d'executar-se “en silenci” ● no imprimir als tests ● usar assercions i excepcions ● Què no cal provar? ● mètodes get/set ● el bon funcionament del compilador 62
    • Millors pràctiques: TDD ● TDD: Test Driven Development ● Escriure primer el test i després el codi que el fa passar: TDD = Test + Code + Refactor ● Potser TDD és massa extrem ● però és convenient crear els tests en paral·lel amb el codi i no com un afegit posterior 63
    • Millors pràctiques: mètodes que llencen excepcions public class TestDefaultController extends TestCase { [...] public void testGetHandlerNotDefined() { TestRequest request = new TestRequest(“testNotDefined”); try { controller.getHandler(request); fail(“An exception should be raised if the requested “ + “handler has not been registered”); } catch (RuntimeException expected) { assertTrue(true); } } } 64
    • Millors pràctiques: crear tests pels “bugs” trobats ● Malgrat els nostres esforços sempre es troben “bugs” en els nostres programes ● Quan això passa el que hem de fer és escriure un test que el manifesta ● òbviament aquest test és necessari ja que el “bug” ha escapat els nostres mecanismes de detecció ● (penseu en els tests com un sedàs que no hauria de deixar passar els errors) ● Ara ja podem arreglar el codi per a que no es produeixi 65 ● i el test assegura que no es reprodueixi
    • Millors pràctiques: dissenyar codi testejable ● Separar interfícies de les classes que les implementen ● podem substituir les implementacions al testejar ● podem usar mocks, stubs, etc. ● No crear objectes que són dependències dins de les classes ● new només s'hauria d'usar en classes factoria ● la idea és que aquests objectes creats amb new no són substituïbles ● o bé usar “injecció de dependències” 66
    • Els diferents tipus de proves dins del cicle de vida ● unitàries ● d'integració ● funcionals ● estrès/càrrega ● acceptació 67
    • Proves d'integració ● Una vegada se sap que les unitats funcionen correctament de forma independent (tests unitaris) cal veure què passa quan interactuen entre sí. ● Podem parlar d'intergració a varis nivells: ● com interactuen els objectes ● com interactuen els serveis (EJBs, BBDD, etc.) ● com interactuen els subsistemes ● Idealment s'haurien de definir abans que es codifiquin les diverses unitats 68
    • Proves funcionals ● Provar les interaccions entre els diferents objectes és essencial però, el resultat esperat de l'aplicació serà el que volem? ● Les proves funcionals verifiquen el comportament a nivell dels casos d'ús definits a l'especificació. ● Si tenim l'aplicació separada en capes fariem proves a nivell dels missatges de sistema ja que aquests encapsulen tota la funcionalitat de l'aplicació. 69
    • Proves d'estrès i càrrega ● És important tenir una aplicació que funciona, però com es comportarà quan la utilitzin molts usuaris simultàniament? ● Normalment es fan amb software especialitzat (p.e. JMeter) que envia peticions i mira quantes son servides. ● Un altre tipus d'anàlisi es pot fer dins de l'entorn de desenvolupament usant un profiler per cercar colls d'ampolla a l'aplicació. ● També hi ha una extensió de JUnit anomenada JUnitPerf per fer proves de rendiment 70
    • Proves d'acceptació ● És important que el rendiment sigui el correcte però el que realment importa és: l'aplicació satisfà les necessitats del client? ● Aquests tests són realitzats pel client (o per algú que el representa). ● Aquests tests involucren també aspectes més subjectius com facilitat d'ús, d'aprenentatge, etc, etc. 71
    • Abast dels tests unitaris ● Encara que JUnit està específicament dissenyat per a realitzar proves unitàries, també podem escriure amb ell d'altres tipus de tests. ● Alguns consideren “unitari” tots aquests tipus de tests i distingeixen entre ● tests unitaris lògics ● tests unitaris d'integració ● tests unitaris funcionals 72
    • Cicle de vida ● Recordeu que estem en un context d'un cicle de vida iteratiu i incrementar (Procés Unificat) ● Per tant, l'activitat de proves es fa a totes les iteracions i fem tots els tipus de tests per a tots els increments ● L'activitat de test (a qualsevol nivell) mai no es pot deixar pel final, just abans de fer el lliurament al client 73
    • Coses que no hem vist ● Integració amb altres eines com ant, maven, etc. ● Generadors d'informes: JUnitReport ● Test dins de contenidors: Cactus ● Anàlisi cobertura: Clover ● ..... 74
    • Bibliografia ● V.Massol, T.Husted. JUnit in Action. Manning (2004). ● A.Hunt, D.Thomas. Pragmatic Unit Testing. The Pragmatic Programmers (2003) ● M.Fowler. Mocks aren't stubs. (Darrera modificació 2-I- 2007) ● Sang Shin, Unit Testing with JUnit Testing framework. (Darrera modificació 25-V-2008). ● D.Bolaños, A.Sierra, M.I.Alarcón. Pruebas de Software y JUnit. Pearson (2008) ● Miško Hevery, The Testability Explorer Blog ● Paulo Caroli, Using JMock with Test Driven Development 75
    • Projectes ● http://www.junit.org/ ● http://www.jmock.org/ ● http://easymock.org/ ● http://mockito.org/ ● http://code.google.com/p/hamcrest/ 76
    • Aquesta obra està subjecta a una llicència Reconeixement- Compartir Igual 3.0 Espanya de Creative Commons. Per veure'n una còpia, visiteu http://creativecommons.org/licenses/by-sa/3.0/es/ o envieu una carta a Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA. 77