【问题标题】:Running integration tests for a spring-boot REST service using gradle使用 gradle 为 spring-boot REST 服务运行集成测试
【发布时间】:2015-10-03 03:09:15
【问题描述】:

我目前正在尝试为基于以下内容的 REST 服务设置集成测试框架:

  1. 弹簧启动
  2. 分级
  3. 码头

我能够使用 spring-boot 集成测试框架spring-boot junit runner 来调出应用上下文并成功运行测试。

接下来我要做的就是创建一个 gradle 任务,它将执行以下操作:

  1. 构建 jar(不是战争)
  2. 启动 jetty 并部署 jar
  3. 针对这个 jar 运行一组测试用例。
  4. 停靠码头

=> 我尝试使用“jetty”插件。但它似乎不支持 jar 文件。
=> 然后我尝试使用 JavaExec 任务 来运行 jar,然后运行测试,但是在测试完成后我找不到直接的方法来停止 jar 进程。
=> Exec 类型的任务也有同样的问题。

所以,我对此有两个问题:

  1. 有没有办法使用 gradle 实现上述形式的集成测试。

  2. 是否推荐这种集成测试方式或有更好的方法?

非常感谢任何想法和见解。

谢谢,

【问题讨论】:

    标签: java gradle spring-boot integration-testing


    【解决方案1】:

    有不同的方法来实现你想要的。我在客户端帮助的方法依赖于 Spring Boot Actuator 提供的 /shutdown URL。 重要提示如果您使用这种方法,请务必将disable or secure the /shutdown endpoint 用于生产。

    在构建文件中你有两个任务:

    task startWebApp(type: StartApp) {
        dependsOn 'assemble'
        jarFile = jar.archivePath
        port = 8080
        appContext = "MyApp"
    }
    
    task stopWebApp(type: StopApp) {
        urlPath = "${startWebApp.baseUrl}/shutdown"
    }
    

    您应该确保您的集成测试依赖于startWebApp 任务,并且它们应该由停止任务完成。所以是这样的:

    integTest.dependsOn "startWebApp"
    integTest.finalizedBy "stopWebApp"
    

    当然,您还需要创建自定义任务实现:

    class StartApp extends DefaultTask {
        static enum Status { UP, DOWN, TIMED_OUT }
    
        @InputFile
        File jarFile
    
        @Input
        int port = 8080
    
        @Input
        String appContext = ""
    
        String getBaseUrl() {
            return "http://localhost:${port}" + (appContext ? '/' + appContext : '')
        }
    
        @TaskAction
        def startApp() {
            logger.info "Starting server"
            logger.debug "Application jar file: " + jarFile
    
            def args = ["java",
                    "-Dspring.profiles.active=dev",
                    "-jar",
                    jarFile.path]
            def pb = new ProcessBuilder(args)
            pb.redirectErrorStream(true)
    
            final process = pb.start()
            final output = new StringBuffer()
            process.consumeProcessOutputStream(output)
    
            def status = Status.TIMED_OUT
            for (i in 0..20) {
                Thread.sleep(3000)
    
                if (hasServerExited(process)) {
                    status = Status.DOWN
                    break
                }
    
                try {
                    status = checkServerStatus()
                    break
                }
                catch (ex) {
                    logger.debug "Error accessing app health URL: " + ex.message
                }
            }
    
            if (status == Status.TIMED_OUT) process.destroy()
    
            if (status != Status.UP) {
                logger.info "Server output"
                logger.info "-------------"
                logger.info output.toString()
                throw new RuntimeException("Server failed to start up. Status: ${status}")
            }
        }
    
        protected Status checkServerStatus() {
            URL url = new URL("$baseUrl/health")
            logger.info("Health Check --> ${url}")
            HttpURLConnection connection = url.openConnection()
            connection.readTimeout = 300
    
            def obj = new JsonSlurper().parse(
                connection.inputStream,
                connection.contentEncoding ?: "UTF-8")
            connection.inputStream.close()
            return obj.status == "UP" ? Status.UP : Status.DOWN
        }
    
        protected boolean hasServerExited(Process process) {
            try {
                process.exitValue()
                return true
            } catch (IllegalThreadStateException ex) {
                return false
            }
        }
    }
    

    请注意,在线程上启动服务器很重要,否则任务永远不会结束。停止服务器的任务更简单:

    class StopApp extends DefaultTask {
    
        @Input
        String urlPath
    
        @TaskAction
        def stopApp(){
            def url = new URL(urlPath)
            def connection = url.openConnection()
            connection.requestMethod = "POST"
            connection.doOutput = true
            connection.outputStream.close()
            connection.inputStream.close()
        }
    }
    

    它基本上向 /shutdown URL 发送一个空的 POST 以停止正在运行的服务器。

    【讨论】:

      猜你喜欢
      • 2017-10-30
      • 2015-08-02
      • 2020-02-10
      • 2015-02-10
      • 2016-06-12
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多