如何在Scala Play单元测试中修改FakeApplication的JDBC URL?
解决Play框架测试中DB URL不兼容的问题
方案1:从配置文件读取DB参数(推荐)
硬编码DB URL会导致生产与测试环境参数不一致,建议从Play配置文件读取参数,实现环境隔离:
- 在
application.conf添加生产环境DB配置:
db.readwrite { url = "jdbc:postgresql://db:5432/postgres" user = "你的用户名" password = "你的密码" driver = "org.postgresql.Driver" }
- 在
application.test.conf添加测试环境配置(覆盖生产URL):
include "application.conf" db.readwrite.url = "jdbc:postgresql://localhost:5432/postgres"
- 修改DB配置类,注入Play的
Configuration读取参数:
import play.api.Configuration import com.zaxxer.hikari.HikariConfig object DBConnectionConfig { def hikariConfig(config: Configuration): HikariConfig = { val dbConfig = config.get[Configuration]("db.readwrite") val hikariConfig = new HikariConfig hikariConfig.setDriverClassName(dbConfig.get[String]("driver")) hikariConfig.setJdbcUrl(dbConfig.get[String]("url")) hikariConfig.setUsername(dbConfig.get[String]("user")) hikariConfig.setPassword(dbConfig.get[String]("password")) hikariConfig } }
- 更新Module,注入
Configuration:
import com.google.inject.{Provides, Singleton} import play.api.Configuration import io.getquill.{PostgresJdbcContext, SnakeCase} import java.time.Duration import com.zaxxer.hikari.HikariDataSource class ReadWriteDB extends ScalaModule { @Singleton @Provides @Named("db.sql.readwrite.quill.context") def quillContext(config: Configuration): PostgresJdbcContext[SnakeCase.type] = { val ds = new HikariDataSource(DBConnectionConfig.hikariConfig(config)) QuillFactory.create(ds, Duration("2m")) } }
- 测试中覆盖配置或指定测试配置文件:
import play.api.test._ import play.api.test.Helpers._ class HealthCheckSpec extends PlaySpec with GuiceOneAppPerTest with Injecting { // 方式A:直接覆盖配置参数 override def fakeApplication(): Application = { GuiceApplicationBuilder() .configure( "db.readwrite.url" -> "jdbc:postgresql://localhost:5432/postgres" ) .build() } // 方式B:加载测试专用配置文件 // override def fakeApplication(): Application = { // GuiceApplicationBuilder() // .loadConfig(Configuration.load(play.api.Environment.simple(mode = Test))) // .build() // } "HealthCheck status" should { "reply from the router" in { val request = FakeRequest(GET, "/status") val home = route(app, request).get status(home) mustBe OK contentAsString(home) must include("API is running!") } } }
方案2:Mock DB依赖(单元测试最佳实践)
健康检查接口无需真实DB连接,可Mock掉QuillContext,避免依赖外部数据库:
import play.api.test._ import play.api.test.Helpers._ import com.google.inject.name.Names import io.getquill.{PostgresJdbcContext, SnakeCase} import org.scalamock.scalatest.MockFactory class HealthCheckSpec extends PlaySpec with GuiceOneAppPerTest with Injecting with MockFactory { override def fakeApplication(): Application = { GuiceApplicationBuilder() // 替换真实QuillContext为Mock实例 .overrides(bind[PostgresJdbcContext[SnakeCase.type]] .annotatedWith(Names.named("db.sql.readwrite.quill.context")) .toInstance(mock[PostgresJdbcContext[SnakeCase.type]])) .build() } "HealthCheck status" should { "reply from the router" in { val request = FakeRequest(GET, "/status") val home = route(app, request).get status(home) mustBe OK contentAsString(home) must include("API is running!") } } }
方案3:临时修改硬编码(不推荐,仅调试用)
若仅临时调试,可通过反射修改硬编码URL,但该方式缺乏扩展性,不建议长期使用:
class HealthCheckSpec extends PlaySpec with GuiceOneAppPerTest with Injecting { override def beforeAll(): Unit = { super.beforeAll() // 反射修改DBConnectionConfig的硬编码URL val field = classOf[DBConnectionConfig.type].getDeclaredField("jdbcUrl") field.setAccessible(true) field.set(null, "jdbc:postgresql://localhost:5432/postgres") } "HealthCheck status" should { "reply from the router" in { val request = FakeRequest(GET, "/status") val home = route(app, request).get status(home) mustBe OK contentAsString(home) must include("API is running!") } } }
内容的提问来源于stack exchange,提问作者Lossan
相关产品推荐
相关产品推荐

