如何为Akka HTTP端点编写集成测试并启动本地应用?
Akka HTTP端点集成测试实现方案
问题背景
在Java中可通过Dropwizard Rule启动本地API服务,直接对本地端点发起请求完成集成测试,同时配置好所需环境变量。现在需要在Akka HTTP中实现类似效果,通过启动本地应用测试/jobs端点,但不清楚如何在测试前启动服务。
控制器代码
@GET @Path("/jobs") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) @Operation( summary = "See Status of All Workflow Jobs", description = "See Status of All Workflow Jobs.", method = "GET", security = Array(new SecurityRequirement(name = "bearerAuth"))) def getJobs: Route = jobs
现有测试代码(未启动服务)
"The service" should { "return jobs response" in { val headers = RawHeader("Authorization", "Bearer token") val httpRequest = HttpRequest( HttpMethods.GET, "http://localhost:8080/jobs?ids=20388241", scala.collection.immutable.Seq(headers)) def sendRequest(): Future[String] = { val responseFuture: Future[HttpResponse] = Http().singleRequest(httpRequest) Await.result(responseFuture, 5.seconds) val entityFuture: Future[HttpEntity.Strict] = responseFuture.flatMap(response => response.entity.toStrict(10.seconds)) entityFuture.map(entity => entity.data.utf8String) } val mapper = new ObjectMapper mapper.registerModule(DefaultScalaModule) sendRequest().map(response => assert( mapper.readValue(response, classOf[WorkflowsResponse]).workflows.get(0).workflowId > 0)) } }
修改后的完整测试代码
使用ScalaTest的BeforeAndAfterAll特质管理服务生命周期,实现测试前启动、测试后关闭本地服务:
import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.model.{HttpMethods, HttpRequest, RawHeader} import akka.http.scaladsl.server.Route import akka.stream.ActorMaterializer import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule import org.scalatest.BeforeAndAfterAll import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import scala.concurrent.duration._ import scala.concurrent.{Await, Future} class JobServiceIntegrationSpec extends AnyWordSpec with Matchers with BeforeAndAfterAll { implicit val system: ActorSystem = ActorSystem("JobServiceTestSystem") implicit val materializer: ActorMaterializer = ActorMaterializer() import system.dispatcher // 替换为你实际的路由实例 val jobRoutes: Route = YourController.getJobs var serverBinding: Http.ServerBinding = _ val testPort = 8080 val baseUrl = s"http://localhost:$testPort" override def beforeAll(): Unit = { // 启动Akka HTTP服务并绑定端口 val bindingFuture = Http().bindAndHandle(jobRoutes, "localhost", testPort) serverBinding = Await.result(bindingFuture, 10.seconds) } override def afterAll(): Unit = { // 关闭服务与ActorSystem,清理资源 val shutdownFuture = serverBinding.unbind().flatMap(_ => system.terminate()) Await.result(shutdownFuture, 10.seconds) } "The jobs service" should { "return valid workflow responses" in { val headers = RawHeader("Authorization", "Bearer token") val httpRequest = HttpRequest( method = HttpMethods.GET, uri = s"$baseUrl/jobs?ids=20388241", headers = Seq(headers) ) def sendRequest(): Future[String] = { Http().singleRequest(httpRequest) .flatMap(_.entity.toStrict(10.seconds)) .map(_.data.utf8String) } val mapper = new ObjectMapper().registerModule(DefaultScalaModule) val response = Await.result(sendRequest(), 10.seconds) val workflowsResponse = mapper.readValue(response, classOf[WorkflowsResponse]) workflowsResponse.workflows.get(0).workflowId should be > 0 } } }
关键说明
- 服务生命周期控制:通过
beforeAll启动服务,afterAll关闭服务与ActorSystem,确保测试前后资源完全清理。 - 路由绑定:将
YourController.getJobs替换为真实的业务路由实例,保证测试逻辑与生产环境一致。 - 异步逻辑优化:移除原测试中冗余的
Await.result调用,通过链式调用简化Future处理;测试断言阶段使用Await.result获取同步结果,适配ScalaTest同步测试范式(若需全异步测试,可改用AsyncWordSpec)。 - 环境变量配置:若需设置测试环境变量,可在
beforeAll中通过System.setProperty加载测试配置:// 设置环境变量 System.setProperty("TEST_ENV_VAR", "test-value") // 或加载测试专属配置文件 val config = ConfigFactory.load("test.conf") implicit val system: ActorSystem = ActorSystem("JobServiceTestSystem", config)
内容的提问来源于stack exchange,提问作者user3897533
相关产品推荐
相关产品推荐

