dicembre 03, 2012

Evitare l'esecuzione di test con JUnit

Chi usa Maven sà già che per evitar di eseguire i test basta scrivere:

    mvn install -DskipTests

(... eh? ma non facevi TDD? Sì sì tranquillo... è più legato ai test di integrazione... quelli lenti? ricordi? quelli che non puoi lanciare quando sei su *quella* macchina? ricordi? continua a leggere, dai! :) )


Nei mesi scorsi, su un progetto dove il 99% dei test erano basati su database e avevano la leggibilità che ha il sanscrito per uno scarafaggio, ho introdotto il maven-failsafe-plugin così da aggiungere un'altra possibilità:





        evitare i test di integrazione 





 


Come? con:

    mvn install -DskipITs

Ora, il fatto è che evitare tutti i test di integrazione puo' esser anche più di quello che ci serve.

Io posso avere un test di integrazione verso un database PostgreSQL e un altro verso un web service che ha sviluppato quel mascalzone del mio concorrente:)
Posso avere un test di integrazione che necessita di internet e un altro che verifica l'autenticazione con un LDAP.




Io non voglio evitare tutti i test di integrazione



Se mi trovo nel mio ufficio potrei esser in grado di lanciare tutti i test di integrazione.
Trovandomi dal cliente potrei aver difficoltà anche solo a trovare un cavo di rete, figuriamoci lanciare dei test:) Però potrei almeno lanciar quei test che, per intenderci, usano solo Java e le risorse interne al progetto (src/test/resources ad esempio se usate Maven).
Su un'isola deserta senza internet, potrei non riuscire a lanciare i test di integrazione che fanno web scraping, ma lanciare comunque quelli di un database PostgreSQL che mi sono installato nella chiavetta USB a forma di cocco.




Non sarebbe utile poter evitare solo quei test di integrazione che *so* non interessarmi o non poter lanciare in un preciso momento?






Io ho pensato ad una possibilità per farlo (se ne conoscete altre, magari più "ufficiali", mandatemi un commento... grazie in anticipo).

Ho pensato di aver un set di opzioni skip<qualcosa>Tests
skipTests li eviterà tutti, ma con skipDbTests eviterò i soli test che necessitano del database... con skipPostgreSQLTests eviterò i soli test che usano PostgreSQL (ma potrei comunque consentire l'esecuzione di quelli che usano MySQL).


Per farlo, ho pensato di utilizzare Assume di JUnit, ma in una maniera un po' differente da quanto suggerito nel Javadoc.
Ipotizziamo di avere un test che legge i dati da un database. Ad es.:

public class UserTest() {
    ...
    @Test public void load() {
        User user = new User("r.simoni");
        assertNull(user.name());

        user.load(aDataSource(), "SELECT name FROM users WHERE login = ?");
        assertEquals("Roberto Simoni", user.name());
    }...
}

posso aggiungere:

public class UserTest() {
    ...
    @Test public void load() {
        Assume.assumeTrue(! Boolean.getBoolean("skipDbTests"));

        User user = new User("r.simoni");
        assertNull(user.name());

        user.load(aDataSource(), "SELECT name FROM users WHERE login = ?");
        assertEquals("Roberto Simoni", user.name());
    }...
}


Così facendo, per evitare l'esecuzione dei soli test di integrazione che necessitano del database basterà scrivere:

    mvn install -DskipDbTests

Nonostante la cosa possa suonare strana, vi invito a prenderla in considerazione.


Immaginate di poter offrire ai colleghi la possibilità di scrivere:

    mvn install \
        -DskipPostgreSQLTests \
        -DskipAppEngineTests \
        -DskipWSTests




Spero di avervi fatto crescere l'interesse, a voi la tastiera...

3 commenti:

  1. Utilizzando la combinazione naming conventions-maven profiles si può ottenere lo stesso risultato evitando di delegare all'implementazione del test il controllo.
    Ad esempio chiamando tutti i test che eseguono chiamate ad un Web Service *WebServiceIT*.java e tutti i test che accedono a Postgresql *PostgresqlIT*.java possiamo pensare alla seguente configurazione maven:

    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <executions>
    <execution>
    <goals>
    <goal>integration-test</goal>
    <goal>verify</goal>
    </goals>
    <configuration>
    <excludes>
    <exclude>**/*WebServiceIT*.java</exclude>
    <exclude>**/*PostgresqlIT*.java</exclude>
    </excludes>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>

    A questo punto aggiungiamo i profili:

    <profiles>
    <profile>
    <id>ws-it</id>
    <activation>
    <property>
    <name>!skipWSTests</name>
    </property>
    </activation>
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <executions>
    <execution>
    <id>run-ws-it</id>
    <goals>
    <goal>integration-test</goal>
    <goal>verify</goal>
    </goals>
    <configuration>
    <includes>
    <includes>**/*WebServiceIT*.java</includes>
    </includes>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    </profile>
    <profile>
    <id>postgresql-it</id>
    <activation>
    <property>
    <name>!skipPostgreSQLTests</name>
    </property>
    </activation>
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <executions>
    <execution>
    <id>run-postgresql-it</id>
    <goals>
    <goal>integration-test</goal>
    <goal>verify</goal>
    </goals>
    <configuration>
    <includes>
    <include>**/*PostgresqlIT*.java</include>
    </includes>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    </profile>
    </profiles>

    A questo punto è possibile lanciare i test:

    mvn verify
    mvn -DskipWSTests -DskipPostgreSQLTests verify

    RispondiElimina
  2. Sempre restando nel campo maven è possibile utilizzare, in sostituzione della naming conventions, l'annotazione @Category (http://kentbeck.github.com/junit/javadoc/4.10/org/junit/experimental/categories/Category.html). In questo modo è possibile avere anche configurazioni più complesse in quanto le categorie sono combinabili.
    In tal caso bisogna utilizzare la configurazione "groups" del maven-failsafe-plugin valorizzando il nome completo di package della classe categoria che si vuole includere (si possono specificare più classi separandole con ",") es:

    <configuration>
    <groups>foo.bar.itest.categories.Postgresql,foo.bar.itest.categories.WebServices</groups>
    </configuration>

    RispondiElimina
  3. Grazie Marco.
    Interessanti queste Category...

    RispondiElimina