如何在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
相关产品推荐
相关产品推荐

