I'm test driven

Search

  • Blogroll

  • Chi sono su LinkedIn

    License

    This blog is licensed under a Creative Commons License

    UnitTest come specifiche, ovvero ridurre le distanze

    February 5th, 2007 by Enri

    Ultimamente si parla spesso di BDD, aka Behoviour Driven Development. Cosa è il BDD? Altro non è che il TDD rivisto nel linguaggio utilizzato, per fornire maggiore focus verso le specifiche.

    I più navigati con il TDD infatti, sanno bene che la T di Test è un po’ fuorviante, dal momento che in realtà ogni metodo di test dovrebbe riflettere una specifica, ed ogni classe di test, avendo in comune i metodi di setUp e di tearDown, dovrebbe incapsulare un preciso stato (o il contesto) dell’oggetto/i sotto test. A questo si può arrivare “meccanicamente” rimuovendo le duplicazioni tra i test e cercando l’estrema chiarezza dei test stessi.
    Questo modo di vedere il TDD ha vari vantaggi, tra i quali, manco a dirlo, il ridurre il gap tra la dichiaratività delle specifiche (dettate dal customer), e la loro implementazione imperativa. In altre parole ridurre la distanza tra il mondo degli sviluppatori (e tester) e quello degli esperti del dominio del quale i primi vogliono essere a supporto.

    Venendo ad un po’ di codice, il primo passo per incamminarsi su questa strada può essere quello di utilizzare un semplice xsl sui report prodotti da JUnit, per cominciare a misurare quanto ad oggi, siamo orientati alle specifiche ed ai comportamenti, invece che alle classi ed ai metodi. Tale xsl assume che ogni test JUnit sia una collezione di specifiche, ad esempio scritta un questo modo:

    1. package org.whoever.foo;
    2.    import junit.framework.TestCase;
    3.    
    4.    public class TestANewlyInitialisedFoo extends TestCase {
    5.          public void testShouldHaveZeroLength() {
    6.                  …
    7.           }
    8.      
    9.           public void testShouldHaveNoMembers() {
    10.               …
    11.           }
    12.  
    13.           public void testShouldThrowExceptionOnPop() {
    14.               …
    15.           }
    16.       }

    ..e produce un report così fatto:

    A newly initialised foo

    * should have zero length
    * should have no members
    * should throw exception on pop

    Non male vero? Immaginiamo ad esempio che il cliente ci chieda di modificare una specifica su un certo scenario: scorrendo il nostro report siamo in grado in tempo zero di identificare quale porzione di codice modificare, o di cogliere eventuali errori di consistenza di specifiche.
    In altre parole non solo stiamo tracciando senza sforzo e molto dettagliatamente le specifiche, ma addirittura le stiamo legando in modo molto forte alla loro implementazione, cosa che i metodi formali hanno sempre cercato di fare, ma con scarsi risultati di praticabilità!

    Un ulteriore passo avanti su questa strada è utilizzare un vero e proprio DSL, in via di implementazione in Java con JDave:

    1. import jdave.Block;
    2. import jdave.Context;
    3. import jdave.Specification;
    4.  
    5. public class StackSpec extends Specification<Stack<?>> {
    6.     public class EmptyStack {
    7.         private Stack<String> stack;
    8.  
    9.         public Stack<String> create() {
    10.             stack = new Stack<String>();
    11.             return stack;
    12.         }
    13.  
    14.         public void isEmpty() {
    15.             specify(should.be.empty());
    16.         }
    17.  
    18.         public void isNoLongerEmptyAfterPush() {
    19.             stack.push("anything");
    20.             specify(should.not().be.empty());
    21.         }
    22.     }
    23.  
    24.     public class FullStack {
    25.         private Stack<Integer> stack;
    26.  
    27.         public Stack<Integer> create() {
    28.             stack = new Stack<Integer>(10);
    29.             for (int i = 0; i < 10; i++) {
    30.                 stack.push(i);
    31.             }
    32.             return stack;
    33.         }
    34.  
    35.         public void isFull() {
    36.             specify(should.be.full());
    37.         }
    38.  
    39.         public void complainsOnPush() {
    40.             specify(new Block() {
    41.                 public void run() throws Exception {
    42.                     stack.push(100);
    43.                 }
    44.             }, should.raise(StackOverflowException.class));
    45.         }
    46.        
    47.         public void containsAllItems() {
    48.             for (int i = 0; i < 10; i++) {
    49.                 specify(stack, contains(i));
    50.             }
    51.         }
    52.        
    53.         public void doesNotContainRemovedItem() {
    54.             stack.remove(3);
    55.             specify(stack, does.not().contain(3));
    56.         }
    57.        
    58.         public void containsAllButRemovedItem() {
    59.             stack.remove(3);
    60.             specify(stack, containsAll(1, 2, 4, 5, 6, 7, 8, 9));
    61.         }
    62.     }
    63. }

    Scusate il mio entusiasmo, ma qui davvero metodi quali il Design By Contract ed il Test Driven Development si sposano in un idillio fatto di alta comprensibilità e garantita affidabilità, perché raggiunte per costruzione incrementale! :)

    Ancora una volta il linguaggio utilizzato non è apparenza, ma sostanza.

    Altro link interessante: Zio Bob parla di BDD.

    Posted in Quality, Test Driven Development (TDD), DSL |

    Leave a Comment

    Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.