这个问题是关于处理混合非接口特征的类的测试,这是包含一些功能的特征.在测试时,类功能应该与混合特性(假设单独测试)提供的功能隔离.
我有一个简单的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()
题
这种方法可以完成这项工作,但需要为每个帮助程序特征设置一个模拟支持特性,这有点单调乏味.但是,我看不到任何其他方式可以工作.
这是正确的方法吗?
如果是,它的目标是否可以更有效地达到(语法魔术,编译器插件等)?
欢迎任何反馈.谢谢!
您可以编写一个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
而mock方法只会替换实例中的helper方法crawlerTestee
.
编辑:我不认为测试类如何与辅助特征交互是一个好主意.在我看来,您应该测试Crawler
行为而不是其内部实现细节.实现可以改变,但行为应尽可能保持稳定.我上面描述的过程允许您覆盖辅助方法,使其确定性并避免真正的网络,从而帮助和加快测试.
但是,我认为测试Helper本身是有意义的,因为它可以在其他地方重复使用并具有适当的行为.