在开始对该服务器进行测试之前,是否有一种简单的方法可以在 sbt 中分叉服务器进程?

Is there a simple way to fork a server process in sbt before starting test against that server?

我的端到端测试任务向服务器发送了一些 http 请求。我想在单独的 jvm 上启动该服务器(基于 Play 框架),然后启动命中服务器的测试并让它完成,然后停止服务器。

到目前为止,我浏览了许多 SO 线程发现了这些选项:

  1. 使用sbt-sequential
  2. 使用sbt-revolver
  3. 使用alias

但在我的实验中设置 fork 不起作用,即它在服务器启动时仍然阻止执行

fork := true
fork in run := true
fork in Test := true
fork in IntegrationTest := true

sbt docs 中的 startServer/stopServer 示例似乎也被阻止了

我也试过从 shell 在后台启动服务器,但服务器很快就关闭了,类似于

nohup sbt -Djline.terminal=jline.UnsupportedTerminal web/run  < /dev/null > /tmp/sbt.log 2>&1 &

相关问题:

fork 不会 运行 并行任务 - 它只是确保测试 运行 在单独的 JVM 中,这有助于关闭 webhooks 或断开与服务的连接没有正确处理资源释放(例如,从不调用断开连接的数据库连接)。

如果你想使用相同的 sbt 来启动服务器并且 运行 测试那个实例(这听起来很容易破解反模式顺便说一句)你可以使用类似的东西:

  • reStart
  • it:test
  • reStop

但是这会很棘手,因为 reStart 会立即产生,因此测试会在服务器设置开始但不一定完成时开始。竞争条件、测试失败或阻止 所有 测试直到服务器完成启动。

这就是为什么没有人这样做的原因。更容易处理的解决方案是:

  • 在某些 beforeAll 方法中启动测试服务器,并仅在服务器响应查询后才完成此方法
  • 在某些 afterAll 方法中关闭它(或者使用 cats.effect.Resource 或类似方法以某种方式处理这两者)
  • 视情况而定:
    • 运行ning 顺序测试以避免同时启动两个实例或
    • 为每个测试生成配置,以便它们可以 运行 并行,而不会在端口分配上发生冲突

其他任何事情都只是一个黑客,迟早会失败。

回答我自己的问题,我们最终做的是

  1. 使用“sbt stage”为 Play 网络应用创建独立服务器 jar 和 运行 脚本(在 web/target/universal/stage/bin/ 中)
  2. 创建 run_integration_tests.sh shell 启动服务器的脚本,等待 30 秒并启动测试
  3. 在调用run_integration_tests.sh的build.sbt中添加运行IntegrationTests任务,并将其添加到it:test

run_integration_tests.sh:

#! /bin/bash
CURDIR=$(pwd)
echo "Starting integration/e2e test runner"
date >runner.log

export JAVA_OPTS="-Dplay.server.http.port=9195 -Dconfig.file=$CURDIR/web/conf/application_test.conf  -Xmx2G" 

rm -f "$CURDIR/web/target/universal/stage/RUNNING_PID"

echo "Starting server"
nohup web/target/universal/stage/bin/myapp >>runner.log 2>&1 &

echo "Webserver PID is $pid"
echo "Waiting for server start"
sleep 30

echo "Running the tests"
sbt "service/test:run-main com.blah.myapp.E2ETest"

ERR="$?"
echo "Tests Done at $(date), killing server"
kill $pid
echo "Waiting for server exit"
wait $pid
echo "All done"
if [ $ERR -ne 0 ]; then
    cat runner.log
    exit "$ERR"
fi

build.sbt:

lazy val runIntegrationTests = taskKey[Unit]("Run integration tests")
runIntegrationTests := {
    val s: TaskStreams = streams.value
    s.log.info("Running integration tests...")
    val shell: Seq[String] = Seq("bash", "-c")
    val runTests: Seq[String] = shell :+ "./run_integration_tests.sh"
    if ((runTests !) == 0) {
        s.log.success("Integration tests successful!")
    } else {
        s.log.error("Integration tests failed!")
        throw new IllegalStateException("Integration tests failed!")
    }
}

lazy val root = project.in(file("."))
  .aggregate(service, web, tools)
  .configs(IntegrationTest)
  .settings(Defaults.itSettings)
  .settings(
    publishLocal := {},
    publish := {},
    (test in IntegrationTest) := (runIntegrationTests dependsOn (test in IntegrationTest)).value
  )

在 CI/jenkins 中调用 sbt:

  sh 'sbt clean coverage test stage it:test'