Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing mockito when()调用如何工作?_Unit Testing_Mocking_Mockito - Fatal编程技术网

Unit testing mockito when()调用如何工作?

Unit testing mockito when()调用如何工作?,unit-testing,mocking,mockito,Unit Testing,Mocking,Mockito,鉴于以下Mockito陈述: when(mock.method()).thenReturn(someValue); 如果mock.method()语句将返回值传递给when(),Mockito如何为mock创建代理?我想这会用到一些CGLib的东西,但我很想知道这在技术上是如何做到的 简单的回答是,在您的示例中,mock.method()的结果将是一个类型适当的空值;mockito通过代理、方法截取和MockingProgress类的共享实例使用间接寻址,以确定在模拟上调用方法是用于存根还是重

鉴于以下Mockito陈述:

when(mock.method()).thenReturn(someValue);

如果mock.method()语句将返回值传递给when(),Mockito如何为mock创建代理?我想这会用到一些CGLib的东西,但我很想知道这在技术上是如何做到的

简单的回答是,在您的示例中,
mock.method()
的结果将是一个类型适当的空值;mockito通过代理、方法截取和
MockingProgress
类的共享实例使用间接寻址,以确定在模拟上调用方法是用于存根还是重放现有存根行为,而不是通过模拟方法的返回值传递有关存根的信息

几分钟后对mockito代码进行的一个小型分析如下。注意,这是一个非常粗略的描述-这里有很多细节。我建议你自己检查一下

首先,当您使用
Mockito
类的
mock
方法模拟一个类时,基本上会发生以下情况:

  • Mockito.mock
    委托给.mock,将默认的mock设置作为参数传递
  • MockitoCore.mock
    委托给.createMock
  • MockUtil
    类使用
    ClassPathLoader
    类获取用于创建模拟的
    MockMaker
    实例。默认情况下,将使用该类
  • CgLibMockMaker
    使用从JMock借用的类,该类负责创建模拟。使用的“mockito魔术”的关键部分是用于创建模拟的
    MethodInterceptor
    :mockito
    MethodInterceptorFilter
    ,以及一系列MockHandler实例,包括的实例。方法拦截器将调用传递给MockHandlerImpl实例,该实例实现了在模拟上调用方法时应应用的业务逻辑(即,搜索以查看是否已记录答案,确定调用是否表示新存根等。默认状态是,如果存根尚未为所调用的方法注册,则返回与类型相应的空值
  • 现在,让我们看一下示例中的代码:

    when(mock.method()).thenReturn(someValue)
    
    以下是此代码的执行顺序:

  • mock.method()
  • when()
  • 。然后返回
  • 理解发生了什么的关键是调用mock上的方法时会发生什么:方法拦截器被传递有关方法调用的信息,并委托给它的
    MockHandler
    实例链,这些实例最终委托给
    MockHandlerImpl#handle
    。在
    MockHandlerImpl#handle
    期间>,模拟处理程序创建
    ongoingstubingimpl
    的实例,并将其传递给共享的
    MockingProgress
    实例


    调用
    method()
    后调用
    When
    方法时,它将委托给
    MockitoCore。When
    ,它调用同一类的方法。此方法从被模拟的
    MockingProgress
    方法()的共享
    MockingProgress
    实例中解压正在进行的存根调用写入并返回它。然后对
    OngoingStubing
    实例调用
    然后返回
    方法。

    简短的回答是,在幕后,Mockito使用某种全局变量/存储来保存方法存根构建步骤的信息(调用方法(),when(),然后返回()在您的示例中),这样最终它就可以建立一个映射,当调用what param时应该返回什么

    我发现这篇文章非常有用: 解释基于代理的模拟框架如何工作()。 作者实现了一个演示模拟框架,我发现这是一个非常好的资源,适合那些想了解这些模拟框架如何工作的人


    在我看来,这是反模式的典型用法。通常我们在实现一个方法时应该避免“副作用”,这意味着该方法应该接受输入并进行一些计算并返回结果——除此之外,没有其他改变。但Mockito只是故意违反了这一规则。它的方法除了返回结果之外还存储了一堆信息结果:Mockito.anyString(),mockInstance.method(),when(),然后返回,它们都有特殊的“副作用”。这也是为什么框架乍看起来像一个魔术——我们通常不会编写这样的代码。然而,在模拟框架的情况下,这种反模式设计是一个伟大的设计,因为它导致了非常简单的API。

    感谢您的详细回答。另一个问题——您提到了that“调用方法()后调用方法的时间”-它如何知道调用方法()的时间()是调用方法()的下一个调用(或包装)?希望这是有意义的。@marchaos它不知道。使用
    when(mock.method()).thenXyz(…)
    语法,
    mock.method()
    在“replay”中执行模式,而不是“stubing”模式。通常情况下,
    mock.method()
    的执行没有效果,因此稍后当执行
    thenXyz(…)
    thenReturn
    thenThrow
    thenAnswer
    等)时,它进入“stubing”状态模式,然后记录该方法调用所需的结果。Rogerio,实际上比这更微妙-mockito没有显式的存根和重播模式。我稍后将编辑我的答案,以便更清楚。总之,使用CGLIB或Javassist在另一个方法中拦截方法调用更容易,因为它可以进行交互ept,比如“if”操作符。我还没有在这里修改我的描述,但我也没有忘记它。仅供参考。非常好的链接。这背后的天才是:非常简单的API,使整个事情看起来非常好。另一个伟大的决定是when()方法使用gener