这是在Play框架的上下文中。我有一个实例化验证器的控制器。 Validator有验证方法。 Controller有一个方法putEnity(),它使用这个validate()验证它接收的有效负载。
对于控制器的单元测试,我想模拟调用Validator.validate()。 TestController看起来像这样
class EntityControllerTest extends FlatSpec with Mockito {
def testPutEntity() = {
val payload = createPayload()
val mockValidator = mock[Validator]
when(mockValidator.validate(anyString, anyString)).thenReturn(EntityValidationResult(true, "Test"))
EntityController.putEntity(payload)
}
问题是,这个模拟调用没有被使用,但实际的validate()被调用,因此测试失败。
我该如何解决这个问题
答案 0 :(得分:0)
问题在于你的最后一行:
EntityController.putEntity(payload)
你有效地在EntityController
上调用了一个静态方法,尽管你试图配置一个模拟Validator
,但你从来没有机会注入它#34;"进入控制器。
如果没有看到你如何实现你的控制器,建议一个理想的解决方案会有点困难,但猜测一下,你想要做类似以下的事情允许一个模拟的Validator
被注入进行测试,但是其他一切都像之前一样工作。
我会逐步完成它(希望)让它更清晰:
EntityController
可实例化:你可能有这样的事情:
object EntityController extends Controller {
...
def putEntity = Action ...
..
}
替换为:
class EntityControl extends Controller {
...
def putEntity = Action ...
...
}
object EntityController extends EntityControl
现在,您已经为自己提供了new
具有与您的"生产相同的行为的能力"宾语。但我们需要能够在模拟验证器中替换......
EntityControl
中的验证程序实例:您的旧EntityController
对象可能有这样的内容:
object EntityController extends Controller {
val validator = new Validator(...)
...
}
这就是为什么你永远不会让你的模拟验证器参与其中。让我们将它作为构造函数参数注入,这样就不会忘记它:
class EntityControl(val validator:Validator) extends Controller {
...
}
object EntityController extends EntityControl(new Validator(...))
再次,我们的"生产" EntityController
对象具有我们过去拥有的所有功能,但关键的区别在于我们已经暴露了"testing seam"以允许在需要时注入模拟。
翻转到您的测试规范,并设置一个可测试的EntityControl
实例
class EntityControllerTest extends FlatSpec with Mockito {
def testPutEntity() = {
val payload = createPayload()
val mockValidator = mock[Validator]
when(mockValidator.validate(anyString, anyString)).thenReturn(EntityValidationResult(true, "Test"))
val myTestableEC = new EntityControl(mockValidator)
myTestableEC.putEntity(payload)
}
在开发更多测试用例时,您可能希望将测试的设置和布线部分提取到合适的函数中,甚至是Specs2 Scope
。
希望这会有所帮助,并为您澄清一些有效的单元测试想法。