如何在Scala测试中模拟Dispatch http客户端?

5

我有一些关于HTTP请求的代码,需要进行单元测试。
因此,我正在尝试使用Scala(2.9.1.final)、Mockito(1.9.0-rc1)和ScalaTest(1.6.1)来模拟dispatch.Http甚至更好的dispatch.HttpExecutor(0.8.5),但是我甚至无法使我的测试代码编译通过。
在MyHttpTest中,我希望能够针对任何HTTP请求接收到特定的HTTP响应:

import org.scalatest.FunSuite
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito.when
import org.mockito.Matchers.any
import dispatch._

class MyHttpTest extends FunSuite with MockitoSugar {
  test("example") {
    val httpMock = mock[HttpExecutor]
    when(httpMock.apply(any(classOf[Handler[String]]))).thenReturn("Some_HTTP_response")
  }
}

但是它会产生编译错误:

error: overloaded method value thenReturn with alternatives:
(httpMock.HttpPackage[String],<repeated...>[httpMock.HttpPackage[String]])org.mockito.stubbing.OngoingStubbing[httpMock.HttpPackage[String]] <and>
(httpMock.HttpPackage[String])org.mockito.stubbing.OngoingStubbing[httpMock.HttpPackage[String]]
cannot be applied to (java.lang.String)
when(httpMock.apply(any(classOf[Handler[String]]))).thenReturn("Some_response")

那么如何模拟调度客户端?

以防对某人有用 - 这里是最终使用 Paul Butcher 的 mock(模拟对象)的代码: println(httpMock(url("http://google.com") as_str)) - aka_sh
2个回答

6

我原本想建议您尝试使用ScalaMock而不是Mockito,因为我错误地认为您遇到的问题是由于Mockito不能真正理解Scala(而ScalaMock从头开始就是用Scala创建的)。但是:

  1. 那不是您的问题,
  2. 结果发现ScalaMock在尝试模拟HttpExecutor时失败了,因为它不知道如何处理包对象中定义的类型(ExceptionListener)。该死!我会尽快解决它-感谢您引起我的注意。

无论如何,您无法创建HttpExecutor#HttpPackage的实例,因为它是一个抽象类型。因此,为了解决这个问题,您需要扩展HttpExecutor并使HttpPackage变为具体类型。例如:

class MyHttpTest extends FunSuite with MockitoSugar {
  trait TestHttpExecutor extends HttpExecutor {
    type HttpPackage[T] = T
  }
  test("example") {
    val httpMock = mock[TestHttpExecutor]
    when(httpMock.apply(any(classOf[Handler[String]]))).thenReturn("Some_HTTP_response")
  }
}

非常感谢,你的代码像应该一样工作(更好地说是编译)!我离你的答案很近,但我不知道如何从HtppPackage中取出真实类型(String)。 - aka_sh
P.S.: 当@davidw建议我在模拟之前扩展外部类时,他是非常正确的! - aka_sh

1

我对Scala一无所知。但是作为一个通则,你不应该试图模拟一个不属于你自己的类;而在上面的HttpExecutor中,它似乎就是这样的一个类。

一个非常适合可测试性的模式是开发一个类,它充当这些类的包装器,但没有任何自己的功能。然后编写(至少)两个单独的测试类 -

  • 一个用于您的包装器类的集成测试类,以确保您正确调用了HttpExecutor;
  • 用于使用您的包装器类的任何类的单元测试类。

集成测试类不应有任何模拟。单元测试将为您的包装器类提供模拟。

如果我误解了您的问题 - 并且HttpExecutor确实是您自己的类 - 那么请再次发布,我会尝试提供不同的答案。


1
首先,你是对的,HttpExecutor - 是一个外部特性(来自dispatch lib)。其次,我绝对同意需要集成测试 - 我已经有了一些。我的愿望是也有一个纯粹的“单元”测试。但对我来说,模拟外部接口/类是完全正常的 - 你只是模拟一些行为,无论它是你的还是不是你的。更重要的是,在这种情况下,扩展HttpExecutor并不能帮助我 - 因为我不知道如何描述行为本身(我尝试过,但得到编译错误=))。 - aka_sh
1
好的,如果你坚持嘲笑不属于你的类型。你可以自己谷歌并找到大量的文献来解释为什么这是一个坏主意。在你的特定情况下,我认为可能发生的情况是HttpExecutor的apply()方法返回的内容不是String,但你试图让它的模拟返回一个String。Mockito不会允许你这样做;模拟返回的值必须与该类的真实对象返回的值具有相同的类型。 - Dawood ibn Kareem
1
  1. 让我们不谈论关于模拟理论的问题 - 因为这不是我的问题的原因。
  2. 再次同意你的观点,代码无法编译是因为 apply() 方法应该返回一些内部类型 *HttpPackage[String]*,但我无法弄清如何使 when-thenReturn 代码实现它。
- aka_sh
因此,您需要制作一些类型为HttpPackage [String]的对象;然后将其传递为参数到thenReturn()。由于我不熟悉Scala或dispatch库,因此无法建议如何构造此对象,但这并不是关于mocking的问题。 - Dawood ibn Kareem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接