SBT 集成测试设置

2024-01-07

我想向我的 SBT + Spray 应用程序添加集成测试阶段。

理想情况下,它就像 Maven 一样,具有以下阶段:

  • compile: 应用程序已构建
  • test:单元测试已运行
  • pre-integration-test:应用程序在单独的进程中启动
  • integration-test:运行集成测试;他们向后台运行的应用程序发出请求并验证是否返回了正确的结果
  • post-integration-test:先前启动的应用程序实例已关闭

我很难让它发挥作用。有我可以遵循的有效示例吗?

1)分离“it”代码库:

我首先添加中显示的代码SBT 文档的“集成测试”部分 http://www.scala-sbt.org/release/docs/Detailed-Topics/Testing#integration-tests到一个新文件project/Build.scala.

这允许我在“src/it/scala”下添加一些集成测试并使用“sbt it:test”运行它们,但我不知道如何添加pre-integration-test hook.

问题“确保“重新启动”任务在其之前自动运行:测试 https://stackoverflow.com/questions/21307984/ensure-re-start-task-automatically-runs-before-ittest“似乎解决了如何设置这样的挂钩,但答案对我不起作用(请参阅我的评论 https://stackoverflow.com/questions/21307984/ensure-re-start-task-automatically-runs-before-ittest#comment33384463_21308900).

另外,将上述代码添加到我的 build.scala 中已经完全停止了“sbt re-start”任务的工作:它尝试以“it”模式运行应用程序,而不是“默认”模式。

2)“测试”代码库中的集成测试:

我正在使用 IntelliJ,单独的“it”代码库确实让它感到困惑。它无法编译该目录中的任何代码,因为它认为所有依赖项都丢失了。

我尝试粘贴“具有共享源的附加测试配置 http://www.scala-sbt.org/release/docs/Detailed-Topics/Testing#additional-test-configurations-with-shared-sources“来自 SBT 文档,但我收到编译错误:

[error] E:\Work\myproject\project\Build.scala:14: not found: value testOptions
[error]         testOptions in Test := Seq(Tests.Filter(unitFilter)),

有我可以遵循的有效示例吗?

我正在考虑放弃通过 SBT 设置它,而是添加一个测试标志来将测试标记为“集成”,并编写一个外部脚本来处理这个问题。


我现在已经编写了自己的代码来执行此操作。 我遇到的问题:

  • 我发现转换我的build.sbt to a project/Build.scala文件修复了大部分编译错误(并且通常使编译错误更容易修复,因为 IntelliJ 可以更轻松地提供帮助)。

  • 我能找到的在后台进程中启动应用程序的最好方法是使用sbt-start-script并在新进程中调用该脚本。

  • 在 Windows 上终止后台进程非常困难。

我的应用程序的相关代码发布在下面,因为我认为有些人遇到过这个问题。 如果有人编写一个 sbt 插件来“正确地”执行此操作,我很想听听。

相关代码来自project/Build.scala:

object MyApp extends Build {
  import Dependencies._

  lazy val project = Project("MyApp", file("."))

    // Functional test setup.
    // See http://www.scala-sbt.org/release/docs/Detailed-Topics/Testing#additional-test-configurations-with-shared-sources
    .configs(FunctionalTest)
    .settings(inConfig(FunctionalTest)(Defaults.testTasks) : _*)
    .settings(
      testOptions in Test := Seq(Tests.Filter(unitTestFilter)),
      testOptions in FunctionalTest := Seq(
        Tests.Filter(functionalTestFilter),
        Tests.Setup(FunctionalTestHelper.launchApp _),
        Tests.Cleanup(FunctionalTestHelper.shutdownApp _)),

      // We ask SBT to run 'startScriptForJar' before the functional tests,
      // since the app is run in the background using that script
      test in FunctionalTest <<= (test in FunctionalTest).dependsOn(startScriptForJar in Compile)
    )
    // (other irrelvant ".settings" calls omitted here...)


  lazy val FunctionalTest = config("functional") extend(Test)

  def functionalTestFilter(name: String): Boolean = name endsWith "FuncSpec"
  def unitTestFilter(name: String): Boolean = !functionalTestFilter(name)
}

该帮助代码位于project/FunctionTestHelper.scala:

import java.net.URL
import scala.concurrent.{TimeoutException, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.sys.process._

/**
 * Utility methods to help with the FunctionalTest phase of the build
 */
object FunctionalTestHelper {

  /**
   * The local port on which the test app should be hosted.
   */
  val port = "8070"
  val appUrl = new URL("http://localhost:" + port)

  var processAndExitVal: (Process, Future[Int]) = null

  /**
   * Unfortunately a few things here behave differently on Windows
   */
  val isWindows = System.getProperty("os.name").startsWith("Windows")

  /**
   * Starts the app in a background process and waits for it to boot up
   */
  def launchApp(): Unit = {

    if (canConnectTo(appUrl)) {
      throw new IllegalStateException(
        "There is already a service running at " + appUrl)
    }

    val appJavaOpts =
      s"-Dspray.can.server.port=$port " +
      s"-Dmyapp.integrationTests.itMode=true " +
      s"-Dmyapp.externalServiceRootUrl=http://localhost:$port"
    val javaOptsName = if (isWindows) "JOPTS" else "JAVA_OPTS"
    val startFile = if (isWindows) "start.bat" else "start"

    // Launch the app, wait for it to come online
    val process: Process = Process(
      "./target/" + startFile,
      None,
      javaOptsName -> appJavaOpts)
        .run()
    processAndExitVal = (process, Future(process.exitValue()))

    // We add the port on which we launched the app to the System properties
    // for the current process.
    // The functional tests about to run in this process will notice this
    // when they load their config just before they try to connect to the app.
    System.setProperty("myapp.integrationTests.appPort", port)

    // poll until either the app has exited early or we can connect to the
    // app, or timeout
    waitUntilTrue(20.seconds) {
      if (processAndExitVal._2.isCompleted) {
        throw new IllegalStateException("The functional test target app has exited.")
      }
      canConnectTo(appUrl)
    }
  }

  /**
   * Forcibly terminates the process started in 'launchApp'
   */
  def shutdownApp(): Unit = {
    println("Closing the functional test target app")
    if (isWindows)
      shutdownAppOnWindows()
    else
      processAndExitVal._1.destroy()
  }

  /**
   * Java processes on Windows do not respond properly to
   * "destroy()", perhaps because they do not listen to WM_CLOSE messages
   *
   * Also there is no easy way to obtain their PID:
   * http://stackoverflow.com/questions/4750470/how-to-get-pid-of-process-ive-just-started-within-java-program
   * http://stackoverflow.com/questions/801609/java-processbuilder-process-destroy-not-killing-child-processes-in-winxp
   *
   * http://support.microsoft.com/kb/178893
   * http://stackoverflow.com/questions/14952948/kill-jvm-not-forcibly-from-command-line-in-windows-7
   */
  private def shutdownAppOnWindows(): Unit = {
    // Find the PID of the server process via netstat
    val netstat = "netstat -ano".!!

    val m = s"(?m)^  TCP    127.0.0.1:${port}.* (\\d+)$$".r.findFirstMatchIn(netstat)

    if (m.isEmpty) {
      println("FunctionalTestHelper: Unable to shut down app -- perhaps it did not start?")
    } else {
      val pid = m.get.group(1).toInt
      s"taskkill /f /pid $pid".!
    }
  }

  /**
   * True if a connection could be made to the given URL
   */
  def canConnectTo(url: URL): Boolean = {
    try {
      url.openConnection()
        .getInputStream()
        .close()
      true
    } catch {
      case _:Exception => false
    }
  }

  /**
   * Polls the given action until it returns true, or throws a TimeoutException
   * if it does not do so within 'timeout'
   */
  def waitUntilTrue(timeout: Duration)(action: => Boolean): Unit = {
    val startTimeMillis = System.currentTimeMillis()
    while (!action) {
      if ((System.currentTimeMillis() - startTimeMillis).millis > timeout) {
        throw new TimeoutException()
      }
    }
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SBT 集成测试设置 的相关文章

随机推荐

  • KMM 在 Android Studio - Windows 版本中工作吗

    我需要为 iOS 和 Android 构建一个应用程序 重新启动 Android Studio 后出现此错误 Kotlin 多平台移动插件问题Kotlin 多平台移动插件应该仅在 macOS 上运行 我使用的是windows机器 请告诉我
  • 在 Oracle pl/sql 中创建或替换表

    我需要一个脚本来创建表 或者如果它已经存在则将其删除 然后重新创建表 经过一番研究后我发现CREATE OR REPLACE TABLE在 pl sql 中不存在 所以我想出了这个脚本 DECLARE does not exist EXCE
  • Sqlite ALTER TABLE - 在现有列之间添加列?

    如果我有一个包含列的表 a b c 然后我执行 ALTER TABLE 命令来添加新列 d 是否可以将其添加到 a 和 b 之间 而不是添加到末尾 我听说列的位置会影响性能 在 SQLite 中 无法使用 ALTER TABLE 语句在两个
  • 有没有struts2 jquery渐进式文件上传插件?

    有没有像 jQuery 一样带有良好进度条的文件上传插件 支持标签库 可以在 Struts2 上运行 你知道吗struts2文件上传插件 http code google com p ajax file upload struts2 这是基
  • Django:icontains 大小写对 unicode 敏感

    我正在对我的博客进行简单的搜索 我使用亚美尼亚语 当我搜索时 这些字母总是有意义的 这是我的代码的一部分 先感谢您 search query get get search query list search query split post
  • 比较 php 中的多维和单维数组

    我是 php 新手 只是在玩一些数组 我想从数组中获取以下不同维度的内容 以下是多维数组 a array array productsid gt 90 CouponID gt 50 array productsid gt 80 Coupon
  • PrettyPhoto Jquery Lightbox 链接问题

    我正在使用非常棒的 PrettyPhoto Lightbox 它是一个 Jquery 灯箱克隆 要使用 iframe 启动灯箱 这是代码 a href http www google com iframe true width 100 he
  • 无法从堆中收集无法访问的对象

    我正在努力解决 JVM 堆 Java 1 7 中无法访问的对象 从图中可以看到 图中所有类都是不可达的 我们有超过74 的对象没有引用 所以应该被垃圾收集 在我们的 tomcat 7 服务器上运行 3 周后就会出现这种状态 该服务器仅运行
  • AS3:如何从事件侦听器功能中的 LoaderInfo 获取动态加载程序 URL?

    我正在加载许多图片 并使用数组来执行此操作 loader i load new URLRequest picture i 我的事件监听器功能是这样启用的 loader i contentLoaderInfo addEventListener
  • 使用 Akka 从 Play 应用程序进行 Web 服务调用

    我对于使用 Play 框架和 Akka 进行编程相当陌生 尽管我已经阅读它们有一段时间了 我现在正在默认 基本 Play 环境上启动一个概念验证应用程序 我的问题源于 Play 中的 Web 服务客户端 api http www playf
  • 由于 IPTABLES 更改,SVN 无法工作

    由于我重新启动了安装了 svn 的 Ubuntu 服务器 因此我无法从笔记本电脑访问它 使用 svnX 当我尝试浏览我的曲目库时 出现错误 svn Can t connect to host xxx xxx Address already
  • 使用开放 API 配置设置全局参数?

    我在用Spring Boot REST OpenAPI 3规格 在此示例中 我希望全局设置标题 Custom Header Version v1 我想在向每个端点发出请求时传递它 现在的问题是我已经100 个 REST 端点对于每个端点我需
  • Spring Data api(包含或 startwith)不适用于具有通配符的 SPACE

    我正在将 Elastic Search 与 Spring Data 结合使用 我在存储库中有一个简单的方法 findByUserNameContaining a b 由于空间原因 此方法未给出预期结果 获取错误 无法构造查询 a b 请改用
  • 在 C# 中简单实现 SHA-3 Keccak 哈希到错误的输出?

    我正在尝试获取 HashLib 库 https hashlib codeplex com https hashlib codeplex com 适用于新的 SHA 3 Keccak 算法 我编写了一个简单的控制台应用程序 据说必须输出正确的
  • CMake、RPATH、$ORIGIN 和 @loader_path

    在我的 CMake 项目中 我构建了一堆在运行时作为插件加载的库 因此 我需要设置各种RPATH 以便动态加载机制可以找到这些库 我还需要整个东西是可重定位的 因为它是 Python 扩展模块的一部分 因此在构建过程中会被各种工具复制 似乎
  • install.packages 中出现错误:未安装软件包“RGtk2”,因为它不是为 UCRT 构建的

    我尝试安装拨浪鼓和 RGtk2 我用了 install packages https cran microsoft com snapshot 2021 12 15 bin windows contrib 4 1 RGtk2 2 20 36
  • SqlParameter和IN语句[重复]

    这个问题在这里已经有答案了 我需要以下查询 createList string commaSeparatedElements SqlCommand query new SqlCommand SELECT FROM table WHERE i
  • Play Framework webservice 教程 scala [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 使用客户端 HTML 表排序对行进行分组

    是否有现有的表格排序库 或者有没有办法配置表格排序器 http tablesorter com docs 每两行排序 或者 是否有更好的方法来语义地表达我的表 以便标准行排序可以工作 我有一个 html 表 看起来像这样 table the
  • SBT 集成测试设置

    我想向我的 SBT Spray 应用程序添加集成测试阶段 理想情况下 它就像 Maven 一样 具有以下阶段 compile 应用程序已构建 test 单元测试已运行 pre integration test 应用程序在单独的进程中启动 i