2017-02-21 1 views
6

Pour avoir une base de données disponible en scalatest avec les évolutions que j'utilise cette extension de la valeur par défaut PlaySpec inspiré par this SO question:tests de jeux avec base de données: « Trop de connexions »

trait ResetDbSpec extends PlaySpec with BeforeAndAfterAll { 
    lazy val appBuilder = new GuiceApplicationBuilder() 
    lazy val injector = appBuilder.injector() 
    lazy val databaseApi = injector.instanceOf[DBApi] 

    override def beforeAll() = { 
    Evolutions.applyEvolutions(databaseApi.database("default")) 
    } 

    override def afterAll() = { 
    Evolutions.cleanupEvolutions(databaseApi.database("default")) 
    databaseApi.database("default").shutdown() 
    } 
} 

Il applique les évolutions de la base de données lorsque la suite démarre, et les retourne quand la suite se termine. Un test ressemble alors

class ProjectsSpec extends ResetDbSpec with OneAppPerSuite { ... 

Après avoir ajouté d'autres tests comme celui-ci, je frappe un point où certains tests qui réussissent quand je les dirige seul, échouer avec cette erreur:

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections"

Comme on peut le voir dans le code ci-dessus, j'ai essayé d'ajouter la ligne

databaseApi.database("default").shutdown() 

dans afterAll() pour atténuer, mais il n'a eu aucun effet. J'ai essayé de ne pas faire de tests en parallèle, mais pas d'effet non plus. Où est-ce que j'ouvre les connexions db sans les fermer, et où dois-je appeler shutdown()?

N.B. J'utilise Play 2.5.10 et Slick 3.1.

+0

L'application tente-t-elle de conserver plus de connexions dans le pool que la base de données ne le permet? –

+0

@MichaelZajac Comment puis-je vérifier cela? – JulienD

+0

Est-ce que changer 'lazy val databaseApi' en def databaseApi' aide? Sinon, vous pouvez vérifier https://www.playframework.com/documentation/2.5.x/SettingsJDBC pour ajuster la taille maximale du pool. En outre, comme @MichaelZajac dit, vous pouvez vérifier votre configuration de base de données locale pour voir combien de connexions il permet. – Eric

Répondre

1

J'ai beaucoup de tests (environ 500) et je ne suis pas cette erreur, la seule différence que j'ai avec votre code est que j'ajouter

databaseApi.database("default").getConnection().close() 

et

Play.stop(fakeApplication) 

pour les tests d'intégration. Dites-moi si cela change quelque chose.

+0

Malheureusement, il n'est pas :(Dès que j'appelle 'databaseApi' dans BeforeAll ou AfterAll j'obtiens l'erreur (c'est-à-dire que le val paresseux est évalué) Peut-être ai-je mal compris, j'ai ajouté ces deux lignes dans afterAll() 'J'ai seulement ~ 120 tests.Peut-être que MySQL, ou le bon pilote pour ça, est pour quelque chose dans ce domaine. – JulienD

+0

Non, je pense que vous avez bien compris. Si cela peut aider, j'utilise PostgreSQL et je ne change rien à la configuration de la base de données, de slick ou de hikariCP (au moins pour les tests) – Simon

1

Bien qu'il ne répond pas à ce qui se passe avec la fuite de connexions, j'ai finalement réussi à pirater autour de cette:

  1. Ajouter jdbc à vous libraryDependencies, même si le Play-Slick FAQ vous dit de ne pas faire it:

    # build.sbt 
    libraryDependencies += jdbc 
    

    Redémarrez sbt pour prendre en compte les modifications. Dans IntelliJ, vous devrez également actualiser le projet.

  2. Désactiver le jdbc le module qui est en conflit avec le jeu-slick (crédits: this SO answer):

    # application.conf 
    play.modules.disabled += "play.api.db.DBModule" 
    

    Au même endroit, vous devriez avoir déjà configuré quelque chose comme

    slick { 
        dbs { 
        default { 
         driver = "slick.driver.MySQLDriver$" 
         db.driver = "com.mysql.jdbc.Driver" 
         db.url = "jdbc:mysql://localhost/test" 
         db.user = "sa" 
         db.password = "" 
        } 
        } 
    } 
    
  3. Maintenant, vous pouvez utiliser play.api.db.Databases de jdbc et sa méthode withDatabase pour lancer l'évolution s.

    import org.scalatest.BeforeAndAfterAll 
    import org.scalatestplus.play.PlaySpec 
    import play.api.db.{Database, Databases} 
    import play.api.db.evolutions.Evolutions 
    
    
    trait ResetDbSpec extends PlaySpec with BeforeAndAfterAll { 
    
        /** 
        * Here we use Databases.withDatabase to run evolutions without leaking connections. 
        * It slows down the tests considerably, though. 
        */ 
    
        private def withTestDatabase[T](block: Database => T) = { 
        Databases.withDatabase(
         driver = "com.mysql.jdbc.Driver", 
         url = "jdbc:mysql://localhost/test", 
         name = "default", 
         config = Map(
         "username" -> "sa", 
         "password" -> "" 
        ) 
        )(block) 
        } 
    
        override def beforeAll() = { 
        withTestDatabase { database => 
         Evolutions.applyEvolutions(database) 
        } 
        } 
    
        override def afterAll() = { 
        withTestDatabase { database => 
         Evolutions.cleanupEvolutions(database) 
        } 
        } 
    
    } 
    
  4. Enfin, des tests d'appels nécessitant une remise à zéro db comme ceci:

    class MySpec extends ResetDbSpec {...} 
    

Bien sûr, il suce répéter cette configuration à la fois dans "application.test.conf" et withDatabase(), plus il mélange deux API différentes, ne parlant pas de performance. il ajoute également ceci avant et après chaque suite, ce qui est gênant:

[info] application - Creating Pool for datasource 'default'
[info] application - Shutting down connection pool.

Si quelqu'un a une meilleure suggestion, s'il vous plaît améliorer cette réponse! J'ai lutté pendant des mois.