Top Banner
Proves de software (en Java amb JUnit) Juan Manuel Gimeno Illa [email protected]
77

Proves de Software (en Java amb JUnit)

May 09, 2015

Download

Education

Apunts de proves unitàries en Java usant JUnit.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Proves de Software (en Java amb JUnit)

Proves de software(en Java amb JUnit)

Juan Manuel Gimeno Illa

[email protected]

Page 2: Proves de Software (en Java amb JUnit)

2

Í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

Page 3: Proves de Software (en Java amb JUnit)

3

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)

Page 4: Proves de Software (en Java amb JUnit)

4

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?

Page 5: Proves de Software (en Java amb JUnit)

5

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;

}

}

Page 6: Proves de Software (en Java amb JUnit)

6

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!!

Page 7: Proves de Software (en Java amb JUnit)

7

Programant una classe de provapublic 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

Page 8: Proves de Software (en Java amb JUnit)

8

Millorant la classe de provapublic class TestCalculator {

private int nbErrors = 0;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();} catch (Throwable e) {

test.nbErrors++;e.printStackTrace();

}if (test.nbErrors > 0) {

throw new RuntimeException(“There were “ + test.nbErrors + “ error(s)”);}

}}

Cada prova estàimplementada en

un mètode

Fàcil afegirnoves proves

Page 9: Proves de Software (en Java amb JUnit)

9

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)

Page 10: Proves de Software (en Java amb JUnit)

10

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, ....

Page 11: Proves de Software (en Java amb JUnit)

11

La prova usant JUnit

● javac -cp ../junit3.8.2/junit.jar *.java

● java -cp .:../junit3.8.2/junit.jar↳junit.swingui.TestRunner TestCalculator

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);

}

}

Page 12: Proves de Software (en Java amb JUnit)

12

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+ + =

Page 13: Proves de Software (en Java amb JUnit)

13

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.)

Page 14: Proves de Software (en Java amb JUnit)

14

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.

Page 15: Proves de Software (en Java amb JUnit)

15

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);

}

Page 16: Proves de Software (en Java amb JUnit)

16

Diagrama de classes

Page 17: Proves de Software (en Java amb JUnit)

17

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)

Page 18: Proves de Software (en Java amb JUnit)

18

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()

Page 19: Proves de Software (en Java amb JUnit)

19

suite() per TestCalculator

public static Test suite() {return new TestSuite(TestCalculator.class);

}

● El mètode estatic que es crearia és equivalent a:

● Que és equivalent a:

public static Test suite() {TestSuite suite = new TestSuite();suite.addTest(new TestCalculator(“testAdd”));return suite;

}

Page 20: Proves de Software (en Java amb JUnit)

20

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;

}}

Page 21: Proves de Software (en Java amb JUnit)

21

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

Page 22: Proves de Software (en Java amb JUnit)

22

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

Page 23: Proves de Software (en Java amb JUnit)

23

Fallida

Page 24: Proves de Software (en Java amb JUnit)

24

Error

Page 25: Proves de Software (en Java amb JUnit)

25

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.

Page 26: Proves de Software (en Java amb JUnit)

26

Interfície TestListener

public interface TestListener {

// Cridat per notificar que s'ha produït un errorvoid addError(Test test, Throwable t);

// Cridat per notificar que s'ha produït una fallidavoid addFailure(Test test, AssertionFailedError e);

// Cridat per notificar la fi d'execució d'un testvoid endTest(Test test);

// Cridat per notificar l'inici d'execució d'un testvoid startTest(Test test);

}

Page 27: Proves de Software (en Java amb JUnit)

27

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

Page 28: Proves de Software (en Java amb JUnit)

28

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.

Page 29: Proves de Software (en Java amb JUnit)

29

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.

Page 30: Proves de Software (en Java amb JUnit)

30

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

Page 31: Proves de Software (en Java amb JUnit)

31

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) {...}

}

Page 32: Proves de Software (en Java amb JUnit)

32

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

Page 33: Proves de Software (en Java amb JUnit)

33

Recordem TestCalculator

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);

}

}

● El que farem es tracejar l'execució de la classe TestCalculator, mitjançant varis diagrames de seqüència

● Recordem el codi de la classe:

Page 34: Proves de Software (en Java amb JUnit)

34

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)

Page 35: Proves de Software (en Java amb JUnit)

35

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;

}

Page 36: Proves de Software (en Java amb JUnit)

36

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.

Page 37: Proves de Software (en Java amb JUnit)

37

Creant un TestResult

Page 38: Proves de Software (en Java amb JUnit)

38

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

Page 39: Proves de Software (en Java amb JUnit)

39

Executant els mètodes de test

Page 40: Proves de Software (en Java amb JUnit)

40

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.

Page 41: Proves de Software (en Java amb JUnit)

41

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));

}}

Page 42: Proves de Software (en Java amb JUnit)

42

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)

Page 43: Proves de Software (en Java amb JUnit)

43

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

Page 44: Proves de Software (en Java amb JUnit)

44

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ó).

Page 45: Proves de Software (en Java amb JUnit)

45

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)

Page 46: Proves de Software (en Java amb JUnit)

46

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

Page 47: Proves de Software (en Java amb JUnit)

47

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

Page 48: Proves de Software (en Java amb JUnit)

48

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

Page 49: Proves de Software (en Java amb JUnit)

49

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);}public int numberSent() {

return messages.size();}

} class OrderStateTester ....

public void testOrderSendsMailIfUnfilled() {Order order = new Order(TALISKER, 51);MailServiceStub mailer = new MailServiceStub();order.setMailer(mailer);order.fill(warehouse);assertEquals(1, mailer.numberSent());

}}

Comprovem que s'ha enviat el

correu

Només implementemla funcionalitat que

necessitem

Page 50: Proves de Software (en Java amb JUnit)

50

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.

Page 51: Proves de Software (en Java amb JUnit)

51

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.

Page 52: Proves de Software (en Java amb JUnit)

52

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());  } .....

Page 53: Proves de Software (en Java amb JUnit)

53

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

Page 54: Proves de Software (en Java amb JUnit)

54

L'exemple usant jMock2

public class OrderInteractionTester extends MockObjectTestCase {

private static final String TALISKER = "Talisker";

public void testFillingRemovesInventoryIfInStock() {//setup - dataOrder order = new Order(TALISKER, 50);Warehouse warehouseMock = mock(Warehouse.class);final Sequence seq = sequence(“seq”);

    //setup – expectationschecking(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());  } .....

Page 55: Proves de Software (en Java amb JUnit)

55

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

Page 56: Proves de Software (en Java amb JUnit)

56

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();    assertTrue(order.isFilled());  }

Page 57: Proves de Software (en Java amb JUnit)

57

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

Page 58: Proves de Software (en Java amb JUnit)

58

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

Page 59: Proves de Software (en Java amb JUnit)

59

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.

Page 60: Proves de Software (en Java amb JUnit)

60

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());

}}

Page 61: Proves de Software (en Java amb JUnit)

61

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);  }}

Page 62: Proves de Software (en Java amb JUnit)

62

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

Page 63: Proves de Software (en Java amb JUnit)

63

Millors pràctiques: TDD

● TDD: Test Driven Development● Escriure primer el test i després el codi que el

fa passar:

● Potser TDD és massa extrem● però és convenient crear els tests en paral·lel amb

el codi i no com un afegit posterior

TDD = Test + Code + Refactor

Page 64: Proves de Software (en Java amb JUnit)

64

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);}

}}

Page 65: Proves de Software (en Java amb JUnit)

65

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● i el test assegura que no es reprodueixi

Page 66: Proves de Software (en Java amb JUnit)

66

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”

Page 67: Proves de Software (en Java amb JUnit)

67

Els diferents tipus de proves dins del cicle de vida

● unitàries● d'integració● funcionals● estrès/càrrega● acceptació

Page 68: Proves de Software (en Java amb JUnit)

68

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

Page 69: Proves de Software (en Java amb JUnit)

69

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ó.

Page 70: Proves de Software (en Java amb JUnit)

70

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

Page 71: Proves de Software (en Java amb JUnit)

71

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.

Page 72: Proves de Software (en Java amb JUnit)

72

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

Page 73: Proves de Software (en Java amb JUnit)

73

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

Page 74: Proves de Software (en Java amb JUnit)

74

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● .....

Page 75: Proves de Software (en Java amb JUnit)

75

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

Page 76: Proves de Software (en Java amb JUnit)

76

Projectes

● http://www.junit.org/● http://www.jmock.org/● http://easymock.org/● http://mockito.org/● http://code.google.com/p/hamcrest/

Page 77: Proves de Software (en Java amb JUnit)

77

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.