我保证,这是一个严重的问题。我花了最后两个小时阅读了我能找到的尽可能多的Mock定义,没有一个向我解释。
我有一个要测试的类,该类需要一个mapper类作为其主要构造函数的一部分:
mike@mike-Inspiron-3543:~/Documents/programming/useful/extractText$ python -V
Python 3.6.0 :: Anaconda custom (64-bit)
在单元测试中,我有以下代码:
open class PoiListViewModel @Inject constructor(
private val mapper: PoiMapper
) : ViewModel() {
我对所有聪明的开发人员的问题是,模拟到底是什么?具体来说,它可以复制多少原始班级?
我告诉你我的假设定义。模拟是伪造的替代类,它代表我的真实类,但除了跟踪向其发送的方法调用之外,它什么都不做。如果我希望模拟具有任何功能,我需要在其中添加该功能。
至少那是我对模拟的无知观点。但是我显然很生气,因为在单元测试中,我的“模拟”映射器类似乎是一个 actual 映射器类。如果调试单元测试,我会看到它遍历了我的mapper类的所有代码。我看到它返回转换后的数据。
这是映射器类代码(如果有关系的话):
//Mock objects needed to instantiate the class under test
@Mock lateinit var mapper: PoiMapper
// Class being tested
lateinit var poiListViewModel: PoiListViewModel
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
poiListViewModel = PoiListViewModel(mapper)
}
有人可以向我解释一下,一个Mockito模拟是多少模拟?我是否没有正确实例化模拟?有人可以给我一个更好的方式来考虑模拟,以便我可以将所有这些都包裹住吗?
答案 0 :(得分:1)
您对模拟的理解是正确的。作为Mockito的实现细节,您正在碰到Kotlin的默认默认行为。
Mockito模拟(不同于Mockito间谍)被定义为代替您的类实例。除非您将它们存根否则返回它们,否则它们会记录所有交互并返回默认的伪值(零,空字符串,空列表,空值等)。您可以通过存根返回值(when
/ thenReturn
),存根方法的特定行为(when
/ thenAnswer
)来确认它们与被测系统正确协作。 ,或检查是否已调用某些方法(verify
并通过(ArgumentCaptor)检索它们被调用的特定实例。
内幕,Mockito通过生成您要模拟的类的子类,并重写所有委托给Mockito内部处理程序的方法来实现此目的。这就是赋予Mockito静默覆盖行为的能力的原因:被测系统认为它正在调用您的依赖关系,但是您的代码正在使用Java的虚拟方法分派来覆盖依赖关系的行为。
这就是窍门:Java方法默认情况下是虚拟的,除非您将它们标记为final
。在Kotlin中,函数为closed by default, unless you mark them open
。 (我将继续称它们为final
,因为这是JVM或Android Dexer中的定义,该定义正在读取Kotlin生成的字节码。)当虚拟机确定引用基于类型时在静态类型上,并且您正在调用final
方法时,允许编译器执行inline that implementation's code (JLS 8.4.3.3),因为您断言该方法不能被覆盖,并且任何试图覆盖该方法的代码都将在编译时失败。 Mockito生成的代码不是用这种方式编译的,Mockito无法检测到这种情况。
在您的情况下,mapToPresentation
不是open
,因此编译器将其视为final
,并且不会保留使Mockito覆盖行为的虚拟方法分派。您对模拟的定义是正确的,您的代码也将是正确的,除了Kotlin是默认关闭的,而Java是默认打开的。此外,您确实确实依赖于函数open
,因为您希望使用与函数中的实现不同的实现来调用它。
平时,您可以只设置要覆盖的所有功能open
,也可以使用Mockito 2.1+'s built-in feature to mock final methods。但是,作为一般的最佳实践,您希望对象的行为类似于PoiMapper,即使它没有遵循特定的PoiMapper实现。这可能是接口/ Impl拆分的一个很好的理由,例如您的PoiListViewModel类采用了PoiMapper接口。在生产中,您可以按需提供PoiMapperImpl,但是Mockito可以生成PoiMapper接口的任意实现,而不必担心PoiMapperImpl及其功能是打开还是关闭。
答案 1 :(得分:0)
您添加了注释
@RunWith(MockitoJUnitRunner.class)
去你的考试班吗?