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
2
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)
3
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?
4
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;
}
}
5
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!!
6
7. 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
8. 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
}
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)
9
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, .... 10
11. 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
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
12
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.)
13
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.
14
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);
}
15
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)
17
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()
18
19. 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
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;
}
}
20
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
21
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 22
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.
25
26. 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
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
27
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.
28
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.
29
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 30
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) {...}
}
31
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
32
33. 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
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)
34
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;
}
35
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.
36
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 38
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.
40
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));
} 41
}
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)
42
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
43
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ó).
44
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)
45
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
46
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
47
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
48
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);
} 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
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.
50
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.
51
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());
52
}
.....
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 53
54. 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
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 55
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(); 56
assertTrue(order.isFilled());
}
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 57
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 58
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.
59
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());
}
} 60
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);
}
}
61
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
62
63. 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
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);
}
}
}
64
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
65
● i el test assegura que no es reprodueixi
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”
66
67. Els diferents tipus de proves dins
del cicle de vida
● unitàries
● d'integració
● funcionals
● estrès/càrrega
● acceptació
67
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 68
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ó.
69
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
70
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.
71
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
72
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
73
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
● .....
74
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
75
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.
77