You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala中Mock requests-scala的POST请求进行单元测试?

问题描述

我用requests-scala模块发送HTTP请求,代码如下:

object AEMUtility {
    ....
    def getJWTToken() {
    ....
    val response = requests.post(
      s"$imsExp/ims/exchange/jwt/",
      headers = Map(
        "Content-Type" -> "application/x-www-form-urlencoded"
      ),
      params = Map(
        "client_id" -> clientId,
        "client_secret" -> clientSecret,
        "jwt_token" -> token
      )
    )
    }
}

我想为单元测试Mock这个POST请求,校验传入Mock函数的参数并返回模拟响应。请问能不能Mock Scala中已安装的依赖?

我尝试了以下代码:

"authorize" should "return JWT token if request is valid" in {
    val postMock = mock[requests.post]

  }

但出现错误:Cannot resolve symbol post


解决方案

可以Mock第三方依赖,但你当前的写法存在问题:requests.post是单例对象requests下的方法,并非可直接Mock的类或特质。要实现需求,需要通过包装第三方API或依赖注入的方式来改造代码,再配合测试框架完成Mock。

步骤1:封装HTTP请求逻辑

先把requests.post的调用封装成一个可注入的特质,让业务代码依赖这个抽象而非具体的第三方实现:

// 定义HTTP客户端抽象特质
trait HttpClient {
  def post(url: String, headers: Map[String, String], params: Map[String, String]): requests.Response
}

// 基于requests-scala实现的具体客户端
class RequestsHttpClient extends HttpClient {
  override def post(url: String, headers: Map[String, String], params: Map[String, String]): requests.Response = {
    requests.post(url, headers = headers, params = params)
  }
}

// 修改AEMUtility,注入HttpClient依赖
object AEMUtility {
  // 用var允许测试时替换客户端
  private var httpClient: HttpClient = new RequestsHttpClient

  def getJWTToken() = {
    // 省略原有逻辑...
    val response = httpClient.post(
      s"$imsExp/ims/exchange/jwt/",
      headers = Map("Content-Type" -> "application/x-www-form-urlencoded"),
      params = Map(
        "client_id" -> clientId,
        "client_secret" -> clientSecret,
        "jwt_token" -> token
      )
    )
    // 省略后续处理逻辑...
  }

  // 测试专用:替换HttpClient
  def setHttpClient(client: HttpClient): Unit = {
    httpClient = client
  }
}

步骤2:Mock封装后的HttpClient

用ScalaMock或Mockito等测试框架,MockHttpClient特质,校验参数并返回模拟响应:

import org.scalamock.scalatest.MockFactory
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class AEMUtilitySpec extends AnyWordSpec with Matchers with MockFactory {
  "getJWTToken" should "return valid token when request params are correct" in {
    // 1. 创建Mock的HttpClient
    val mockHttpClient = mock[HttpClient]
    // 2. 让AEMUtility使用Mock客户端
    AEMUtility.setHttpClient(mockHttpClient)

    // 3. 模拟响应对象
    val mockResponse = mock[requests.Response]
    (mockResponse.text _).expects().returning("mock-jwt-token-123")

    // 4. 校验传入的请求参数是否符合预期,并返回模拟响应
    val expectedUrl = "https://example.com/ims/exchange/jwt/"
    val expectedHeaders = Map("Content-Type" -> "application/x-www-form-urlencoded")
    val expectedParams = Map(
      "client_id" -> "test-client-id",
      "client_secret" -> "test-secret",
      "jwt_token" -> "test-raw-jwt"
    )

    (mockHttpClient.post _)
      .expects(expectedUrl, expectedHeaders, expectedParams)
      .returning(mockResponse)

    // 5. 调用测试方法并断言结果
    val result = AEMUtility.getJWTToken()
    result shouldBe "mock-jwt-token-123"
  }
}

为什么直接Mockrequests.post不行?

requests是Scala单例对象,它的post方法属于静态绑定的成员方法,Mock框架无法直接替换这类方法的实现。只有基于抽象特质的实例方法,才能被Mock框架拦截并替换行为。

内容的提问来源于stack exchange,提问作者Shivam Sahil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:35:26