我想学习播放Scala的单元测试,但我遇到了一些问题。 我想在我的模型层这样运行了几个测试:
"User Model" should {
"be created and retrieved by username" in {
running(FakeApplication()) {
val newUser = User(username = "weezybizzle",password = "password")
User.save(newUser)
User.findOneByUsername("weezybizzle") must beSome
}
}
"another test" in {
running(FakeApplication()) {
// more tests involving adding and removing users
}
}
}
但是做事情的时候就这样,我无法连接到第二单元测试数据库,他说,连接被关闭。 我试图通过封闭在同一假的应用程序运行一个块中的所有代码来解决这个问题,但也不能工作。
running(FakeApplication()) {
"be created and retrieved by username" in {
val newUser = User(username = "weezybizzle",password = "password")
User.save(newUser)
User.findOneByUsername("weezybizzle") must beSome
}
"another test" in {
// more tests involving adding and removing users
}
}
Answer 1:
该specs2测试默认情况下,在平行这可能会导致问题访问数据库,尤其是当你依赖于以前的测试中提供的DB内容执行。 如此给力顺序测试你必须告诉specs2这样做:
class ModelSpec extends Specification with Logging {
override def is = args(sequential = true) ^ super.is
...
}
对于一个做的测试FakeApplication
你可以用它在整个测试:
running(FakeApp) {
log.trace("Project tests.")
val Some(project) = Project.findByName("test1")
"Project" should {
"be retrieved by name" in {
project must beAnInstanceOf[Project]
project.description must endWith("project")
}
整个样本,可以发现在这里 。 那是我第一次来处理问题,而与播放测试MongoDB的尝试! 框架。
第二种方法我从借来的萨拉特项目,该项目是由道路很好的处理MongoDB的规格例子源(尽管它不是一个玩!框架应用程序)。 你必须定义延伸的特质Around
和Scope
,在这里你可以把任何你需要在一个应用实例进行初始化:
import org.specs2.mutable._
import org.specs2.execute.StandardResults
import play.api.mvc._
import play.api.mvc.Results
import play.api.test._
import play.api.test.Helpers._
trait FakeApp extends Around with org.specs2.specification.Scope {
val appCfg = Map(
"first.config.key" -> "a_value",
"second.config.key" -> "another value"
)
object FakeApp extends FakeApplication(
additionalPlugins = Seq("com.github.rajish.deadrope.DeadropePlugin"),
additionalConfiguration = appCfg
) {
// override val routes = Some(Routes)
}
def around[T <% org.specs2.execute.Result](test: => T) = running(FakeApp) {
Logger.debug("Running test ==================================")
test // run tests inside a fake application
}
}
编辑2013-06-30:
在当前版本的specs2
在around
签名应该是:
def around[T : AsResult](test: => T): Result
编辑结束
然后,测试可以写成这样:
class SomeSpec extends Specification { sequential // according to @Eric comment
"A test group" should {
"pass some tests" in new FakeApp {
1 must_== 1
}
"and these sub-tests too" in {
"first subtest" in new FakeApp {
success
}
"second subtest" in new FakeApp {
failure
}
}
}
}
这种套件的完整样本,可以发现在这里 。
最后值得注意的是:这也是很好的开始一套之前清理测试数据库:
step {
MongoConnection().dropDatabase("test_db")
}
Answer 2:
虽然这样做集成测试/运行测试套件,我们遇到了像expections或“的SQLException:试图获得从已经被关闭池中的连接”,“在CacheManager中已关闭它不能再被使用。” 他们都涉及到重新启动每次测试后的应用程序。 我们终于做了一个相当简单的特点,将检查每个测试之前运行FakeApplication,只有在需要一个开始。
trait SingleInstance extends BeforeExample {
def before() {
if (Play.unsafeApplication == null) Play.start(AppWithTestDb)
}
}
object AppWithTestDb extends FakeApplication(additionalConfiguration =
Map("db.default.url" -> "jdbc:mysql://localhost/test_db")
)
然后在测试:
class SampleSpec extends PlaySpecification with SingleInstance {
"do something" should {
"result in something" in {
}
}
}
这将为游戏2.3工作,以及播放2.4
Answer 3:
一个稍微更简洁的方法
import play.api.test._
trait ServerSpec {
implicit val app: FakeApplication = FakeApplication()
implicit def port: Port = Helpers.testServerPort
val server = TestServer(port, app)
}
再搭配使用
class UsersSpec extends PlaySpecification with Results with ServerSpec {
"Users Controller" should {
step(server.start())
"get users" in {
val result = Users.query().apply(FakeRequest())
val json = contentAsJson(result)
val stat = status(result)
stat mustEqual 200
}
step(server.stop())
}
}
Answer 4:
为了测试你对数据库代码,在你的情况下使用提供了-MEM一个测试,你应该告诉它在running
的呼叫:
FakeApplication(additionalConfiguration = inMemoryDatabase())
以一定的方式,这将迫使你的数据库,以启动和停止围绕内部块执行(无论是单个或组成)
编辑
由于评论说,您使用的是MongoDB的,我建议你阅读这篇博客 ,其中,我说的是一个小插件,我写的,以使MongoDB的服务器来启动嵌入式等。
我们会做的是(通过启用插件)开始,并在应用的同时停止的mongodb。
它可以帮助你...
然而对于最初的问题,除非玩,萨拉特或任何其他相关插件是做不好的连接或缓存或问题不应该从运行或FakeApplication,来...
Answer 5:
在许多情况下,使用运行方法时,这种并行测试的问题发生。 但是,这已经是固定在play2.1。 这里是如何解决。 如果你想使用这个play2.0.x运行,你应该特质是这样的:
trait TestUtil {
/**
* Executes a block of code in a running application.
*/
def running[T](fakeApp: FakeApplication)(block: => T): T = {
synchronized {
try {
Play.start(fakeApp)
block
} finally {
Play.stop()
play.core.Invoker.system.shutdown()
play.core.Invoker.uninit()
}
}
}
/**
* Executes a block of code in a running server.
*/
def running[T](testServer: TestServer)(block: => T): T = {
synchronized {
try {
testServer.start()
block
} finally {
testServer.stop()
play.core.Invoker.system.shutdown()
play.core.Invoker.uninit()
}
}
}
}
你可以使用以下命令:
class ModelSpec extends Specification with TestUtil {
"User Model" should {
"be created and retrieved by username" in {
running(FakeApplication()) {
val newUser = User(username = "weezybizzle",password = "password")
User.save(newUser)
User.findOneByUsername("weezybizzle") must beSome
}
}
}
....
Answer 6:
我发现运行由斯卡拉单个测试类FakeApplication最好的办法,是按照下面的例子。 注意the'step”的方法:
@RunWith(classOf[JUnitRunner])
class ContaControllerSpec extends MockServices {
object contaController extends ContaController with MockAtividadeService with MockAccountService with MockPessoaService with MockTelefoneService with MockEmailService{
pessoaService.update(PessoaFake.id.get, PessoaFake) returns PessoaFake.id.get
}
step(Play.start(new FakeAppContext))
"ContaController [Perfil]" should {
"atualizar os dados do usuario logado e retornar status '200' (OK)" in {
val response = contaController.savePerfil()(FakeRequest(POST, "/contas/perfil").withFormUrlEncodedBody(
("nome", "nome teste"), ("sobrenome", "sobrenome teste"), ("dataNascimento", "1986-09-12"), ("sexo", "M")).withLoggedIn(config)(uuid))
status(response) must be equalTo(OK)
}
"atualizar os dados do usuario logado enviando o form sem preenchimento e retornar status '400' (BAD_REQUEST)" in {
val response = contaController.savePerfil()(FakeRequest(POST, "/contas/perfil").withLoggedIn(config)(uuid))
status(response) must be equalTo(BAD_REQUEST)
}
}
step(Play.stop)
}
Answer 7:
接受的答案并没有帮助我。 我玩2.2.3阶2.10.3。 这是帮助我。
可能是它可能有一定的帮助。
扩展BoneCPPlugin
class NewBoneCPPlugin(val app: play.api.Application) extends BoneCPPlugin(app) {
override def onStop() {
//don't stop the BoneCPPlugin
//plugin.onStop()
}
}
而在你testspec应
class UserControllerSpec extends mutable.Specification with Logging with Mockito {
val fakeApp = FakeApplication(additionalConfiguration = testDb,withoutPlugins = Seq("play.api.db.BoneCPPlugin"),
additionalPlugins = Seq("NewBoneCPPlugin"))
"Create action in UserController " should {
"return 400 status if request body does not contain user json " in new WithApplication(fakeApp) {
...
}
}
}
文章来源: running multiple tests within the same FakeApplication() in play 2.0 scala