SlideShare uma empresa Scribd logo
1 de 77
Baixar para ler offline
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

Mais conteúdo relacionado

Último

MECANISMES I CINEMÀTICA 1r DE BATXILLERAT
MECANISMES I CINEMÀTICA 1r DE BATXILLERATMECANISMES I CINEMÀTICA 1r DE BATXILLERAT
MECANISMES I CINEMÀTICA 1r DE BATXILLERATLasilviatecno
 
Creu i R.pdf, anàlisis d'una obra de selectivitat
Creu i R.pdf, anàlisis d'una obra de selectivitatCreu i R.pdf, anàlisis d'una obra de selectivitat
Creu i R.pdf, anàlisis d'una obra de selectivitatLourdes Escobar
 
Menú maig 24 escola ernest Lluch (1).pdf
Menú maig 24 escola ernest Lluch (1).pdfMenú maig 24 escola ernest Lluch (1).pdf
Menú maig 24 escola ernest Lluch (1).pdfErnest Lluch
 
XARXES UBANES I LA SEVA PROBLEMÀTICA.pptx
XARXES UBANES I LA SEVA PROBLEMÀTICA.pptxXARXES UBANES I LA SEVA PROBLEMÀTICA.pptx
XARXES UBANES I LA SEVA PROBLEMÀTICA.pptxCRIS650557
 
ELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdf
ELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdfELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdf
ELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdfErnest Lluch
 
SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,
SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,
SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,Lasilviatecno
 

Último (8)

MECANISMES I CINEMÀTICA 1r DE BATXILLERAT
MECANISMES I CINEMÀTICA 1r DE BATXILLERATMECANISMES I CINEMÀTICA 1r DE BATXILLERAT
MECANISMES I CINEMÀTICA 1r DE BATXILLERAT
 
Creu i R.pdf, anàlisis d'una obra de selectivitat
Creu i R.pdf, anàlisis d'una obra de selectivitatCreu i R.pdf, anàlisis d'una obra de selectivitat
Creu i R.pdf, anàlisis d'una obra de selectivitat
 
Menú maig 24 escola ernest Lluch (1).pdf
Menú maig 24 escola ernest Lluch (1).pdfMenú maig 24 escola ernest Lluch (1).pdf
Menú maig 24 escola ernest Lluch (1).pdf
 
XARXES UBANES I LA SEVA PROBLEMÀTICA.pptx
XARXES UBANES I LA SEVA PROBLEMÀTICA.pptxXARXES UBANES I LA SEVA PROBLEMÀTICA.pptx
XARXES UBANES I LA SEVA PROBLEMÀTICA.pptx
 
itcs - institut tècnic català de la soldadura
itcs - institut tècnic català de la soldaduraitcs - institut tècnic català de la soldadura
itcs - institut tècnic català de la soldadura
 
HISTÒRIES PER A MENUTS II. CRA Serra del Benicadell.pdf
HISTÒRIES PER A MENUTS II. CRA  Serra del Benicadell.pdfHISTÒRIES PER A MENUTS II. CRA  Serra del Benicadell.pdf
HISTÒRIES PER A MENUTS II. CRA Serra del Benicadell.pdf
 
ELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdf
ELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdfELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdf
ELLUCHINFORME_BAREM_DEFINITIU_BAREM (1).pdf
 
SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,
SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,
SISTEMA DIÈDRIC. PLANS, PAREL·LELISME,PERPENDICULARITAT,
 

Destaque

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Destaque (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Proves de Software (en Java amb JUnit)

  • 1. Proves de software (en Java amb JUnit) Juan Manuel Gimeno Illa jmgimeno@diei.udl.cat
  • 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
  • 23. Fallida 23
  • 24. Error 24
  • 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
  • 76. Projectes ● http://www.junit.org/ ● http://www.jmock.org/ ● http://easymock.org/ ● http://mockito.org/ ● http://code.google.com/p/hamcrest/ 76
  • 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