Scala中的单元测试助手或非接口特征

时间:2011-08-14 21:31:04

标签: unit-testing scala traits

这个问题是关于处理混合非接口特征的类的测试,这是包含某些功能的特征。在测试时,类功能应该与混合特性(假设单独测试)提供的功能隔离。

我有一个简单的Crawler类,它依赖于HttpConnection和HttpHelpers实用程序函数集合。现在让我们关注HttpHelpers。

在Java 中,HttpHelpers可能是一个实用程序类,并且会将其单例作为依赖项传递给Crawler,无论是手动还是使用某些IoC框架。测试Crawler非常简单,因为依赖很容易被模拟。

在Scala 中,似乎帮助器特征是构成功能的更首选方式。实际上,它更容易使用(扩展时自动导入命名空间的方法,可以使用withResponse ...而不是httpHelper.withResponse ...等)。但它如何影响测试呢?

这是我提出的解决方案,但遗憾的是它将一些样板提升到测试端。

助手特质:

trait HttpHelpers {
  val httpClient: HttpClient
  protected def withResponse[A](resp: HttpResponse)(fun: HttpResponse => A): A = // ...
  protected def makeGetRequest(url: String): HttpResponse = // ...
}

要测试的代码:

class Crawler(val httpClient: HttpClient) extends HttpHelpers {
  // ...
}

测试:

// Mock support trait
// 1) Opens up protected trait methods to public (to be able to mock their invocation)
// 2) Forwards methods to the mock object (abstract yet)
trait MockHttpHelpers extends HttpHelpers {
  val myMock: MockHttpHelpers
  override def makeGetRequest(url: String): HttpResponse = myMock.makeGetRequest(url)
}

// Create our mock using the support trait
val helpersMock = Mockito.mock(classOf[MockHttpHelpers])

// Now we can do some mocking
val mockRequest = // ...
Mockito when (helpersMock.makeGetRequest(Matchers.anyString())) thenReturn mockRequest

// Override Crawler with the mocked helper functionality
class TestCrawler extends Crawler(httpClient) with MockHttpHelpers {
  val myMock = helpersMock
}

// Now we can test
val crawler = new TestCrawler()
crawler.someMethodToTest()

问题

这种方法可以完成这项工作,但需要为每个帮助者特征设置模拟支持特征有点单调乏味。但是,我无法看到任何其他方式来实现这一点。

  • 这是正确的方法吗?
  • 如果是,可以更有效地实现其目标(语法魔术,编译器插件等)吗?

欢迎任何反馈。谢谢!

2 个答案:

答案 0 :(得分:5)

你可以编写一个Helper模拟特征,它应该与HttpHelpers混合并用模拟等价物覆盖它的方法:

trait HttpHelpersMock { this: HttpHelpers =>

  //MOCK IMPLEMENTATION
  override protected def withResponse[A](resp: HttpResponse)(fun: HttpResponse => A): A = // ...

  //MOCK IMPLEMENTATION
  override protected def makeGetRequest(url: String): HttpResponse = // ...
}

然后,在测试爬虫时,在实例化时混合模拟特征:

val crawlerTestee = new Crawler(x) with HttpHelpersMock

模拟方法只会替换实例crawlerTestee中的辅助方法。

编辑:我认为测试一个类如何与辅助特征进行交互并不是一个好主意。在我看来,您应该测试Crawler行为,而不是其内部实现细节。实现可以改变,但行为应尽可能保持稳定。我上面描述的过程允许你覆盖辅助方法,使它们具有确定性,避免真正的网络,从而帮助和加快测试。

但是,我认为测试Helper本身是有意义的,因为它可以在其他地方重复使用并且具有适当的行为。

答案 1 :(得分:3)

怎么样:

val helpers = new HttpHelpers {
  //override or define stuff the trait needs to work properly here
}

helpers.someMethodToTest

看,马云,没有中间特征和模拟库需要!

我一直这样做我的特质,我对结果非常满意。