2013-06-24 29 views
5

Tôi muốn chạy thử nghiệm đơn vị cho ứng dụng Play 2 Scala bằng cách sử dụng cùng một thiết lập cơ sở dữ liệu như được sử dụng trong sản xuất: Slick with Postgres. Sau đây thất bại với "java.sql.SQLException: Cố gắng để có được một kết nối từ một hồ bơi đã được tắt máy." trong bài kiểm tra thứ 2.Chơi 2.1 Kiểm tra Đơn vị Với Slick và Postgres

package controllers 

import org.specs2.mutable._ 
import play.api.db.DB 
import play.api.Play.current 
import play.api.test._ 
import play.api.test.Helpers._ 
import scala.slick.driver.PostgresDriver.simple._ 

class BogusTest extends Specification { 

    def postgresDatabase(name: String = "default", 
         options: Map[String, String] = Map.empty): Map[String, String] = 
    Map(
     "db.test.driver" -> "org.postgresql.Driver", 
     "db.test.user"  -> "postgres", 
     "db.test.password" -> "blah", 
     "db.test.url"  -> "jdbc:postgresql://localhost/blah" 
    ) 

    def fakeApp[T](block: => T): T = 
    running(FakeApplication(additionalConfiguration = 
     postgresDatabase("test") ++ Map("evolutionplugin" -> "disabled"))) { 
     def database = Database.forDataSource(DB.getDataSource("test")) 
     database.withSession { implicit s: Session => block } 
     } 

    "Fire 1" should { 
    "do something" in fakeApp { 
     success 
    } 
    } 

    "Fire 2" should { 
    "do something else" in fakeApp { 
     success 
    } 
    } 
} 

tôi chạy thử nghiệm như thế này:

$ play -Dconfig.file=`pwd`/conf/dev.conf "test-only controllers.BogusTest" 

Hai bí ẩn khác:

1) Tất cả các bài kiểm tra chạy, mặc dù tôi yêu cầu chỉ BogusTest để chạy

2) application.conf luôn được sử dụng, không phải def.conf, và thông tin trình điều khiển đến từ application.conf, không phải thông tin được cấu hình trong mã.

+0

Xem http://stackoverflow.com/questions/15399161/how-do-i-specify-a-config-file-with-sbt-0-12-2-for-sbt-test để biết thông tin về cách chỉ định tập tin cấu hình khác với 'test'. –

+0

Xin cảm ơn, có vẻ như điều này sẽ giải quyết vấn đề bí ẩn # 2. Bạn có thể làm sáng tỏ vấn đề chính không? –

+0

Tôi cũng đang gặp sự cố tương tự. Ngay cả khi không sử dụng đặc điểm Around từ specs2, và chỉ đơn giản là gói trong 'in {running (fakeApp) {Database.forDataSource (DB.getDataSource()) withSession {}}}' cũng đưa ra ngoại lệ này. – Meredith

Trả lời

4

Đây là câu trả lời dự kiến ​​như tôi hiện đang thử nghiệm trên bản phát 2.2.0 và tôi không thể tạo lại lỗi của bạn, sử dụng cơ sở dữ liệu MYSQL.

Tôi cảm thấy có thể có một lỗi rất phức tạp trong mã của bạn. Trước hết, nếu bạn khám phá triển khai DBPlugin do Play cung cấp, BoneCPPPlugin:

/** 
    * Closes all data sources. 
    */ 
    override def onStop() { 
    dbApi.datasources.foreach { 
     case (ds, _) => try { 
     dbApi.shutdownPool(ds) 
     } catch { case NonFatal(_) => } 
    } 
    val drivers = DriverManager.getDrivers() 
    while (drivers.hasMoreElements) { 
     val driver = drivers.nextElement 
     DriverManager.deregisterDriver(driver) 
    } 
    } 

Bạn thấy phương thức onStop() đóng hồ bơi kết nối. Vì vậy, nó rõ ràng, bạn đang cung cấp cho các ví dụ thử nghiệm thứ hai một ứng dụng đã được dừng lại (và do đó các plugin của nó được dừng lại và hồ bơi kết nối db đóng).

Scalatests và specs2 chạy thử nghiệm song song, và bạn có thể dựa vào các helper thử nghiệm bởi vì nó là thread-safe:

def running[T](fakeApp: FakeApplication)(block: => T): T = { 
     synchronized { 
      try { 
      Play.start(fakeApp) 
      block 
      } finally { 
      Play.stop() 
      play.api.libs.ws.WS.resetClient() 
      } 
     } 
     } 

Tuy nhiên, khi bạn làm

DB.getDataSource("test") 

Từ mã nguồn của Play:

def getDataSource(name: String = "default")(implicit app: Application): DataSource = app.plugin[DBPlugin].map(_.api.getDataSource(name)).getOrElse(error) 

Vì vậy, ở đây có ẩn, không được giải quyết cho FakeApplication (đó là n ot một ẩn trong phạm vi !!!), nhưng để Play.current và nó xuất hiện trong trường hợp thứ hai, đây không phải là những gì bạn mong đợi nó được, Play.current vẫn trỏ đến trường hợp trước của FakeApplication: nó có thể phụ thuộc vào cách ngầm đang bị bắt trong đóng cửa

Nếu bạn tuy nhiên, cấu trúc lại các phương pháp fakeApp, bạn có thể đảm bảo các ứng dụng mà bạn vừa tạo ra được sử dụng để giải quyết ngầm (bạn luôn có thể làm sáng tỏ những giá trị cho một tham số ngầm)

def fakeApp[T](block: => T): T = { 
    val fakeApplication = FakeApplication(additionalConfiguration = 
     postgresDatabase("test") ++ Map("evolutionplugin" -> "disabled")) 
     running(fakeApplication) { 
     def database = Database.forDataSource(DB.getDataSource("test")(fakeApplication)) 
     database.withSession { implicit s: Session => block } 
     } 
    } 
+0

Edmondo, tuyệt vời! Điều này làm mọi thứ tiến lên. Bây giờ tôi nhận được "RuntimeException: Không có ứng dụng bắt đầu" khi chạy từ dòng lệnh, thật không may. Tôi đã thực hiện một ý chính với tất cả các tệp có liên quan https://gist.github.com/mslinn/8046542 –

+0

bạn có thể cung cấp dự án sbt trên repo github không? Tôi thiếu application.conf được nhập khẩu trong test.conf và sẽ là tooh ard để gỡ lỗi. Trong ý chính của bạn, tôi thấy bạn vẫn còn play.api.Play.current dưới dạng một lần nhập. Tôi sẽ xóa nó để xem điều gì xảy ra – Edmondo1984

+0

Hãy thử lại với thay đổi tôi đã thực hiện tại đây - https://gist.github.com/drstevens/8048116/f181c671c64df210e37197a30a5801e25f6014fc – drstevens

Các vấn đề liên quan