2013-02-12 7 views
9

È possibile accedere al nome del test attualmente in esecuzione, all'interno di un test ScalaTest? (E come dovrei farlo?)Accedere al nome del test ScalaTest dal test interno?

Background:

sto testando che la mia Data Access Object alla fine genera un OverQuotaException se un utente ad esempio crea troppe pagine. Questi test richiedono piuttosto tempo per essere eseguiti. Per sentirmi più felice, mi piacerebbe stampare i progressi sullo stdout - e poiché ci sono molti test, mi piacerebbe includere il nome del test nell'output, quindi so quale test è attualmente in esecuzione.

(non ho trovato alcuna funzione apparentemente rilevanti qui: http://www.artima.com/docs-scalatest-2.0.M5/#org.scalatest.FreeSpec)

Esempio:

"QuotaCharger can" - { 
    "charge and decline quota consumers" - { 

     "charge a per site IP number (guest user)" in { 
     // ... Here, a guest user post very many comments until it's over quota. 
     // This takes a little while, and there are many similar tests. 

     // ---> Here <--- I'd like to access the string: 
     // "charge a per site IP number (guest user)", 
     // is that possible somehow? 
     } 

risposta

8

Il modo previsto per farlo è sovrascrivere con Fixture e acquisire i dati del test. In questo caso d'uso, è meglio eseguire l'override con Fixture in fixture. FreeSpec in modo da poter passare i dati di test in ogni test piuttosto che utilizzare una var. Info su che è qui:

http://www.artima.com/docs-scalatest-2.0.M5/org/scalatest/FreeSpec.html#withFixtureNoArgTest

Quando ho visto la tua domanda questa mattina mi sono reso conto ScalaTest dovrebbe avere una caratteristica che fa questo, quindi ho solo aggiunto uno. Sarà in 2.0.M6, la prossima versione milestone, ma nel frattempo è possibile utilizzare una copia locale. Eccolo:

import org.scalatest._ 

/** 
* Trait that when mixed into a <code>fixture.Suite</code> passes the 
* <code>TestData</code> passed to <code>withFixture</code> as a fixture into each test. 
* 
* @author Bill Venners 
*/ 
trait TestDataFixture { this: fixture.Suite => 

    /** 
    * The type of the fixture, which is <code>TestData</code>. 
    */ 
    type FixtureParam = TestData 

    /** 
    * Invoke the test function, passing to the the test function to itself, because 
    * in addition to being the test function, it is the <code>TestData</code> for the test. 
    * 
    * <p> 
    * To enable stacking of traits that define <code>withFixture(NoArgTest)</code>, this method does not 
    * invoke the test function directly. Instead, it delegates responsibility for invoking the test function 
    * to <code>withFixture(NoArgTest)</code>. 
    * </p> 
    * 
    * @param test the <code>OneArgTest</code> to invoke, passing in the 
    * <code>TestData</code> fixture 
    */ 
    def withFixture(test: OneArgTest) { 
    withFixture(test.toNoArgTest(test)) 
    } 
} 

Si potrebbe usare in questo modo:

import org.scalatest._ 

class MySpec extends fixture.FreeSpec with TestDataFixture { 
    "this technique" - { 
    "should work" in { td => 
     assert(td.name == "this technique should work") 
    } 
    "should be easy" in { td => 
     assert(td.name == "this technique should be easy") 
    } 
    } 
} 
+0

Penso di preferire l'approccio 'def currentTestName: String' in realtà, che puoi accedere da qualsiasi luogo senza passare alcun parametro (e modificando le firme delle funzioni solo a scopo di debug). Dal mio punto di vista, il nome del test non fa realmente parte del test: si tratta solo di informazioni di debug. Ma quando appare come un parametro del test ('td =>') allora sembra che faccia parte del test. – KajMagnus

+1

Cosa succede se il test richiede un parametro di fissaggio "reale"? Quindi o avresti bisogno di accettare 2 parametri per ogni test? '" nome test "in {case (td, realTestData) => ...}'. O forse non sarebbe possibile passare anche 'realTestData' al test? Perché il solo parametro passato al test sarebbe quello che ha fornito il nome del test (a scopo di debug). – KajMagnus

+0

Bene, tieni presente che currentTestName funziona solo se i test vengono eseguiti in sequenza, il che sarà vero a meno che non si mischi in ParallelTestExecution. In tal caso, puoi semplicemente impostare una var in withFixture in FreeSpec regolare. Se i test vengono eseguiti in modo sequenziale, anche l'approccio hat funzionerà e sembrerà meno invadente se questo è solo per il debugging temporaneo. Supponevo che sarebbe stato lì per sempre. –

2

Crea il tuo carattere, diciamo RichFreeSpec.

trait RichFreeSpec extends Free { 
    protected final class RichFreeSpecStringWrapper(name: scala.Predef.String) { 
    def in(f: String => scala.Unit) { 
     def f2 = f(name) 
     new WordSpecStringWrapper(string).in(f2) 
    } 
    } 

    protected implicit def convertToRichFreeSpecStringWrapper(n: scala.Predef.String): = { 
    new RichFreeSpecStringWrapper(n) 
    } 
} 

Than basta usare:

"sth" in { testName => 
    ... 
} 

Naturalmente, si può andare oltre e mettere in atto la gerarchia nome completo.

+0

Grazie! In realtà non ha funzionato: ogni volta che viene eseguito "il nome del test più recente" in {...} "," currentTestName "è cambiato in" nome test più recente "e quando i blocchi di test effettivi (all'interno di" {.. .} '), il' currentTestName' è sempre lo stesso, ovvero '" nome test più recente "'. – KajMagnus

+0

Grazie alla tua risposta, ho trovato le classi pertinenti e ho potuto inventare qualcosa :-) Quindi +1, perché la risposta è stata utile. (... accetterò la mia risposta dopo 2 giorni (qualche limite)) – KajMagnus

+0

Ci scusiamo per il mio errore. Fisso. – Jakozaur

1

Ecco una soluzione. Estendi questa classe invece di FreeSpec. Licenza: CC0.

Modifica: questo non funziona con test concorrenti però.

(La differenza tra questo approccio e l'altra risposta, è che 1) Qui c'è un campo currentTestName, e nell'altra risposta il nome del test è passato al corpo di prova, e 2) il nome del test include tutti i test nomi dei rami concatenati + il nome del test vero e proprio, mentre il nome del test di altra risposta è esattamente il nome del test (senza nomi filiali di prova).)

(Ops, avresti bisogno di utilizzare getOrElse ... al posto di mia bella getOrDie.)

/** 
* Adds a field `currentTestName` that you can use inside a FreeSpec test, 
* if you for example have many tests that take rather long, and you wonder 
* which one is currently running. 
*/ 
trait RichFreeSpec extends FreeSpec { 

    private var _currentTestName: Option[String] = None 
    def currentTestName = _currentTestName getOrDie "DwE90RXP2" 

    protected override def runTest(testName: String, args: org.scalatest.Args) { 
    _currentTestName = Some(testName) 
    super.runTest(testName, args) 
    } 
}