15

Play 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
        }
    }
}

ただし、この方法で行うと、接続が閉じられていると言って、2番目の単体テストでデータベースに接続できません。同じ偽のアプリケーションで実行されるブロックにすべてのコードを囲むことでこれを解決しようとしましたが、それもうまくいきませんでした。

  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
    }
  }
4

7 に答える 7

15

specs2 テストはデフォルトで並行して実行されるため、特に以前のテストで提供された db コンテンツに依存している場合、データベースへのアクセスで問題が発生する可能性があります。したがって、順次テストを強制するには、specs2 にそうするように指示する必要があります。

class ModelSpec extends Specification with Logging {
  override def is = args(sequential = true) ^ super.is
...
}

1 つのテストで実行される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")
      }

サンプル全体はここにあります。これは、Play で MongoDB をテストする際の問題に対処する最初の試みでした! フレームワーク。

2 番目のアプローチは、MongoDB を扱う仕様例の非常に優れたソースであるsalatプロジェクトから借りたものです (ただし、これは Play! フレームワーク アプリではありません)。Aroundandを拡張する特性を定義する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")
  }
于 2012-08-20T22:23:26.250 に答える
1

ややクリーンなアプローチ

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())
  }
}
于 2014-01-16T09:03:44.627 に答える
0

Scala で単一のテスト クラス FakeApplication を実行するために私が見つけた最良の方法は、以下の例に従うことです。「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)
}
于 2013-12-20T16:49:38.320 に答える
0

受け入れられた答えは私を助けませんでした。私は2.2.3 scala 2.10.3をプレイしています。これが私を助けたものです。

ひょっとしたら何かの役に立つかもしれません。

BoneCPPlugin の拡張

class NewBoneCPPlugin(val app: play.api.Application) extends BoneCPPlugin(app) {


  override def onStop() {
    //don't stop the BoneCPPlugin
    //plugin.onStop()
  }
}

そして、あなたのテストスペックには

    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) {
        ...
    }
  }
}
于 2014-07-02T16:13:11.643 に答える
0

この種の並列テストの問題は、多くの場合、実行中のメソッドを使用するときに発生します。しかし、これは play2.1 ですでに修正されています。修正方法は次のとおりですこれを play2.0.x で実行する場合は、次のような trait を作成する必要があります。

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
            }
        }
    }
    ....
于 2013-01-06T08:02:28.313 に答える
0

データベースに対してコードをテストするには、提供されたメモリ内のものを使用してテストする場合は、running呼び出しでそれを伝える必要があります。

FakeApplication(additionalConfiguration = inMemoryDatabase())

特定の方法で、これにより、データベースが内部ブロック実行の前後で強制的に開始および停止されます (単一か構成かに関係なく)。

編集

mongodb を使用しているというコメントがあるため、このブログを読むことをお勧めします。このブログでは、mongodb サーバーを組み込みのように起動できるようにするために作成した小さなプラグインについて説明しています。

私たちが行うことは、(プラグインを有効にすることによって) アプリケーションと同時に mongodb を開始および停止することです。

それはあなたを助けることができます...

ただし、最初の質問に関しては、Play-Salat またはその他の関連プラグインが接続不良またはキャッシングを行っていない限り、問題は実行中または FakeApplication に起因するものではありません。

于 2012-08-19T20:06:38.847 に答える