这个问题是关于处理混合非接口特征的类的测试,这是包含某些功能的特征。在测试时,类功能应该与混合特性(假设单独测试)提供的功能隔离。
我有一个简单的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()
问题
这种方法可以完成这项工作,但需要为每个帮助者特征设置模拟支持特征有点单调乏味。但是,我无法看到任何其他方式来实现这一点。
欢迎任何反馈。谢谢!
答案 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
看,马云,没有中间特征和模拟库需要!
我一直这样做我的特质,我对结果非常满意。