简体   繁体   English

Mockito模拟有多少模拟?

[英]How much of a mock is a Mockito mock?

This is a serious question, I promise. 我保证,这是一个严重的问题。 I've spent the last 2 hours reading as many definitions of Mock that I could find and none explain this to me. 我花了最后两个小时阅读了我能找到的尽可能多的Mock定义,但没有一个向我解释。

I've got a class I want to test and that class requires a mapper class as part of it's primary constructor: 我有一个要测试的类,该类需要一个mapper类作为其主要构造函数的一部分:

open class PoiListViewModel @Inject constructor(
        private val mapper: PoiMapper
) : ViewModel() {

In my unit test I have the following code: 在单元测试中,我有以下代码:

//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)
}

My question to you all smart developers is, what exactly is a mock? 我对所有聪明的开发人员的问题是,什么是模拟游戏? And specifically how much of my original class does it replicate? 具体来说,它可以复制多少原始班级?

I'll tell you my assumed definition. 我会告诉你我的假设定义。 A mock is a fake stand-in class that stands in for my real class, but that it does nothing except keep track of what method calls get sent to it. 模拟是一个伪造的替代类,它代表我的真实类, 但它除了跟踪要向其发送哪些方法调用之外,什么也不做。 If I want the mock to have any functionality I need to stub that functionality in. 如果我希望模拟具有任何功能,则需要将该功能存入其中。

At least that's my ignorant view of mocks. 至少那是我对模拟的无知观点。 But I'm apparently wront because in my unit test, my "mock" mapper class seems to be an actual mapper class. 但是我显然很生气,因为在我的单元测试中,我的“模拟”映射器类似乎是一个实际的映射器类。 If I debug my unit test I see it walk through all the code of my mapper class. 如果调试单元测试,我会看到它遍历了我的mapper类的所有代码。 I see it returning converted data. 我看到它返回转换后的数据。

Here's the mapper class code (if it matters): 这是mapper类代码(如果有关系的话):

open class PoiMapper @Inject constructor() {

    fun mockTest(num: Int): Int{
        return num *23
    }

    fun mapToPresentation(domainModel: Poi_Domain): Poi_Presentation {

        var test = 3
        var results = mockTest(test)

        return Poi_Presentation(domainModel.id,domainModel.name,domainModel.description,
                domainModel.img_url,domainModel.latitude,domainModel.longitude,domainModel.imgFocalpointX,
                domainModel.imgFocalpointY,domainModel.collection,domainModel.collectionPosition,
                domainModel.release,domainModel.stampText)
    }
}

Can someone explain it to me, how much of a mock is a Mockito mock? 有人可以向我解释一下,Mockito模拟有多少模拟? Did I instantiate the mocks incorrectly? 我是否没有正确实例化模拟? Can someone give me a better way to think of mocks so I can wrap my head around all this? 有人可以给我一个更好的方式来思考模拟,以便我可以将所有这些都包裹住吗?

Your understanding of mocks is correct. 您对模拟的理解是正确的。 You're bumping into Kotlin's final-by-default behavior, as an implementation detail of Mockito. 作为Mockito的实现细节,您正在碰到Kotlin的默认默认行为。

Mockito mocks (as distinct from Mockito spies) are defined to take the place of your class instance. Mockito模拟(不同于Mockito间谍)被定义为代替您的类实例。 Unless you've stubbed them to return otherwise, they record all interactions and return default dummy values (zero, an empty string, an empty list, null, etc). 除非您将它们存根否则返回它们,否则它们会记录所有交互并返回默认的伪值(零,空字符串,空列表,空值等)。 You can confirm that they collaborated correctly with your system-under-test by stubbing return values ( when / thenReturn ), stubbing specific behaviors for methods ( when / thenAnswer ), or by checking that certain methods were called ( verify ) and retrieving the specific instances they were called with (ArgumentCaptor). 您可以通过对返回值( when / thenReturn )进行存根,对方法的特定行为( when / thenAnswer )或检查某些方法是否被调用( verify )并检索特定的方法来确认它们与被测系统正确协作。它们被(ArgumentCaptor)调用的实例。

Under the hood , Mockito achieves this by generating a subclass of the class you're mocking, and overriding all methods to delegate to Mockito's internal handler. 幕后,Mockito通过生成您要模拟的类的子类,并重写所有委托给Mockito内部处理程序的方法来实现此目的。 This is what gives Mockito the power to override the behavior silently: Your system-under-test thinks it's calling your dependency, but your code is using Java's virtual method dispatch to override your dependency's behavior. 这就是赋予Mockito静默覆盖行为的能力的原因:被测系统认为它正在调用您的依赖关系,但是您的代码正在使用Java的虚拟方法分派来覆盖依赖关系的行为。

Here's the trick: Java methods are virtual by default, unless you mark them final . 诀窍是:Java方法默认是虚拟的,除非您将它们标记为final In Kotlin, functions are closed by default, unless you mark them open . 在Kotlin中,功能默认情况下关闭的,除非您将其标记为open (I'm going to keep calling them final , because that's the definition at play in the JVM or Android Dexer, which is reading the bytecode that Kotlin generates anyway.) When the virtual machine is sure of a reference's type based on static typing, and you're calling a final method, the compiler is allowed to inline that implementation's code (JLS 8.4.3.3) because you've asserted that the method cannot be overridden and any code that tries to override it will fail at compilation. (我将继续称它们为final ,因为这是JVM或Android Dexer中正在起作用的定义,该定义正在读取Kotlin生成的字节码。)当虚拟机确定基于静态类型的引用的类型时,并且您正在调用final方法,则允许编译器内联该实现的代码(JLS 8.4.3.3),因为您断言该方法不能被覆盖,并且任何尝试覆盖该方法的代码都将在编译时失败。 Mockito's generated code isn't compiled this way, and Mockito can't detect this case. Mockito生成的代码不是用这种方式编译的,Mockito无法检测到这种情况。

In your case, mapToPresentation is not open , so the compiler sees it as final and does not keep the virtual method dispatch that would let Mockito override the behavior. 在您的情况下, mapToPresentationopen ,因此编译器将其视为final ,并且不保留将使Mockito覆盖行为的虚拟方法分派。 Your definition of mocking is right, and your code would be right, except that Kotlin is closed-by-default where Java is open-by-default. 您对模拟的定义是正确的,您的代码也将是正确的,除了Kotlin是默认关闭的,而Java是默认打开的。 Besides, you really do rely on the function being open , because you'd like it to be called with a different implementation than the one in the function. 此外,您确实依赖于open的函数,因为您希望使用与函数中的实现不同的实现来调用它。

Trivially, you could just make all functions open that you intend to override, or use Mockito 2.1+'s built-in feature to mock final methods . 琐碎地,您可以open要覆盖的所有功能,或者使用Mockito 2.1+的内置功能模拟最终方法 However, as a general best practice you want an object that behaves like a PoiMapper even if it doesn't follow your specific PoiMapper implementation. 但是,作为一般的最佳实践,您希望对象的行为类似于PoiMapper,即使它没有遵循特定的PoiMapper实现。 That might be a good reason for an interface/impl split, such that your class PoiListViewModel takes a PoiMapper interface. 这可能是接口/ Impl拆分的一个很好的理由,例如您的PoiListViewModel类采用了PoiMapper接口。 In production you can provide a PoiMapperImpl as you have, but Mockito can generate an arbitrary implementation of the PoiMapper interface without worrying about whether PoiMapperImpl and its functions are open or closed. 在生产中,您可以按需提供PoiMapperImpl,但是Mockito可以生成PoiMapper接口的任意实现,而不必担心PoiMapperImpl及其功能是打开还是关闭。

Have you added the annotation 您是否添加了注释

@RunWith(MockitoJUnitRunner.class)

to your test class? 参加你的考试班?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM