mockito中两种部分mock的实现,spy、callRealMethod

什么是类的部分mock(partial mock)?
A:部分mock是说一个类的方法有些是实际调用,有些是使用mockito的stubbing(桩实现)。

为什么需要部分mock?

A:当需要测试一个组合方法(一个方法需要其它多个方法协作)的时候,某个叶子方法(只供别人调用,自己不依赖其它反复)已经被测试过,我们其实不需要再次测试这个叶子方法,so,让叶子打桩实现返回结果,上层方法实际调用并测试。

mockito实现部分mock的两种方式:spy和callRealMethod()

spy实现:

package spy;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.LinkedList;
import java.util.List;

import org.junit.Test;

public class SpyDemo {

    @Test
    public void spy_Simple_demo(){
        List<String> list = new LinkedList<String>();
        List<String> spy = spy(list);
        when(spy.size()).thenReturn(100);  

        spy.add("one");
        spy.add("two");  

/*        spy的原理是,如果不打桩默认都会执行真实的方法,如果打桩则返回桩实现。
        可以看出spy.size()通过桩实现返回了值100,而spy.get(0)则返回了实际值*/
        assertEquals(spy.get(0), "one");
        assertEquals(100, spy.size());
    }

    @Test
    public void spy_Procession_Demo() {
        Jack spyJack = spy(new Jack());
        //使用spy的桩实现实际还是会调用stub的方法,只是返回了stub的值
        when(spyJack.go()).thenReturn(false);
        assertFalse(spyJack.go()); 

        //不会调用stub的方法
        doReturn(false).when(spyJack).go();
        assertFalse(spyJack.go());
    } 

}

class Jack {
    public boolean go() {
        System.out.println("I say go go go!!");
        return true;
    }
}

Spy类就可以满足我们的要求。如果一个方法定制了返回值或者异常,那么就会按照定制的方式被调用执行;如果一个方法没被定制,那么调用的就是真实类的方法。

如果我们定制了一个方法A后,再下一个测试方法中又想调用真实方法,那么只需在方法A被调用前,调用Mockito.reset(spyObject);就行了。

package spy;

import static org.mockito.Mockito.when;

import org.mockito.Mockito;

public class TestMockObject {

    public static void main(String[] args) {

        TestMockObject mock = Mockito.mock(TestMockObject.class);
        System.out.println(mock.test1());
        System.out.println(mock.test2());

        TestMockObject spy = Mockito.spy(new TestMockObject());
        System.out.println(spy.test1());
        System.out.println(spy.test2());

        when(spy.test1()).thenReturn(100);
        System.out.println(spy.test1());

        Mockito.reset(spy);
        System.out.println(spy.test1());
        System.out.println(spy.test2());

        when(spy.test1()).thenReturn(104);
        System.out.println(spy.test1());
    }

    public int test1() {
        System.out.print("RealTest1()!!! - ");
        return 1;
    }

    public int test2() {
        System.out.print("RealTest2()!!! - ");
        return 2;
    }

}

输出为:

0
0
RealTest1()!!! - 1
RealTest2()!!! - 2
RealTest1()!!! - 100
RealTest1()!!! - 1
RealTest2()!!! - 2
RealTest1()!!! - 104

要注意的是,对Spy对象的方法定制有时需要用另一种方法:
===============================================================================
Importantgotcha on spying real objects!

Sometimes it‘s impossible to usewhen(Object) for stubbing spies. Example:
List list = new LinkedList();
List spy = spy(list);

//Impossible: real method is called so spy.get(0) throwsIndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
===============================================================================
因为用when(spy.f1())会导致f1()方法被真正执行,所以就需要另一种写法。

http://blog.csdn.net/dc_726/article/details/8568537

callRealMethod()实现

Use doCallRealMethod() when you want to call the real implementation of a method.

As usual you are going to read the partial mock warning: Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects. How does partial mock fit into this paradigm? Well, it just doesn‘t... Partial mock usually means that the complexity has been moved to a different method on the same object. In most cases, this is not the way you want to design your application.

However, there are rare cases when partial mocks come handy: dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.) However, I wouldn‘t use partial mocks for new, test-driven & well-designed code.

See also javadoc Mockito.spy(Object) to find out more about partial mocks. Mockito.spy() is a recommended way of creating partial mocks. The reason is it guarantees real methods are called against correctly constructed object because you‘re responsible for constructing the object passed to spy() method.

Example:


   Foo mock = mock(Foo.class);
   doCallRealMethod().when(mock).someVoidMethod();

   // this will call the real implementation of Foo.someVoidMethod()
   mock.someVoidMethod();
 

See examples in javadoc for Mockito class

Returns:
stubber - to select a method for stubbing
package callRealMethod;

import org.junit.Test;
import static org.mockito.Mockito.*;

public class CallMethodDemo {
    @Test
    public void callRealMethodTest() {
        Jerry jerry = mock(Jerry.class);  

        doCallRealMethod().when(jerry).goHome();
        doCallRealMethod().when(jerry).doSomeThingB();  

        jerry.goHome();  

        verify(jerry).doSomeThingA();
        verify(jerry).doSomeThingB();
    }
}

class Jerry {
    public void goHome() {
        doSomeThingA();
        doSomeThingB();
    }  

    // real invoke it.
    public void doSomeThingB() {
        System.out.println("good day");  

    }  

    // auto mock method by mockito
    public void doSomeThingA() {
        System.out.println("you should not see this message.");  

    }
}

通过代码可以看出Jerry是一个mock对象, goHome()和doSomeThingB()是使用了实际调用技术,而doSomeThingA()被mockito执行了默认的answer行为(这里是个void方法,so,什么也不干)。

总结:

    spy和callrealmethod都可以实现部分mock,唯一不同的是通过spy做的桩实现仍然会调用实际方法(我都怀疑这是不是作者的bug)。

   ★ 批注:spy方法需要使用doReturn方法才不会调用实际方法。

mock技术是实施TDD过程必备的装备,熟练掌握mockito(或者其他工具)可以更有效的进行测试。虽然mockito作者也觉得部分测试不是好的设计,但是在java这样一个不是完全面向对象技术的平台上,我们其实没必要过分纠结这些细节,简洁,可靠的代码才是我们需要的。

http://heipark.iteye.com/blog/1496603

时间: 2024-12-13 01:33:24

mockito中两种部分mock的实现,spy、callRealMethod的相关文章

Javascript中两种最通用的定义类的方法

在Javascript中,一切都是对象,包括函数.在Javascript中并没有真正的类,不能像C#,PHP等语言中用 class xxx来定义.但Javascript中提供了一种折中的方案:把对象定义描述为对象的配方(先看一下例子会比较容易理解).定义类的方法有很多种,这里有两中较为通用的方法,大家参考一下.这两种方法均可以解决 构造函数会重复生成函数,为每个对象都创建独立版本的函数的问题.解决了重复初始化函数和函数共享的问题??提示音:http://www.huiyi8.com/tishiy

struts2中两种validation.xml的配置方式_百度文库

在struts中,根据配置的validation.xml文件进行页面输入项目的验证已经众所周知,本文介绍在struts2中两种validation.xml的配置方式.可以根据不同的需要进行不同的配置. 以下以login页面输入firstname,lastname,和age为例进行说明.struts.xml中,成功的话转向成功页面.不成功的话转回到原页面. 首先建立userbean文件.文件名:UserBean.java包:struts2.login.bean文件内容:package struts

MyEclipse10中两种FreeMarker插件的安装与配置

--------------------非原创 第一个插件是:freemarker-ideMyEclipce10.0中安装FreeMarker插件,这绝对是最简单的方法.步骤如下:(一)打开http://sourceforge.net/projects/freemarker-ide/files/ 下载最新版本,目前本人下载时最新版本是:freemarker-ide-0.9.14(二)将其解压,将hudson.freemarker_ide_0.9.14文件夹放入MyEclipse安装目录dropi

List集合中两种遍历方式

遍历List集合中的元素的方法有两种: 第一种:利用迭代器遍历 代码1: // 迭代器 Iterator it=list.iterator(); while(it.hasNext()) { System.out.println(it.next()); } 或者代码2: for(Iterator it=list.iterator();it.hasNext();) { System.out.println(it.next()); }// 与while循环相比优点:对象it在循环结束后,变为垃圾,自动

JAVA 中两种判断输入的是否是数字的方法__正则化_

JAVA 中两种判断输入的是否是数字的方法 package t0806; import java.io.*; import java.util.regex.*; public class zhengzehua_test { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub try { System.out.println("请输入第一个数字:"

java中两种添加监听器的策略

/*第一种:将事件的处理委托给其他对象,下面的例子是委托给了MyListener(implements ActionListener)*/ 1 import java.applet.Applet; 2 import java.awt.event.*; 3 import java.awt.*; 4 public class ChangeColor extends Applet{//Applet的默认布局为FlowLayout方式 5 Color myColor; 6 String str; 7 B

JavaScript中两种类型的全局对象/函数(转)

转自:http://www.cnblogs.com/snandy/archive/2011/03/19/1988626.html 这里所说的JavaScript指浏览器环境中的包括宿主环境在内的.第一种是ECMAScript Global Object,第二种是宿主环境(Host)下的全局对象/函数. 一.核心JavaScript内置对象,即ECMAScript实现提供的不依赖于宿主环境的对象 这些对象在程序执行之前就已经(实例化)存在了.ECMAScript称为The Global Objec

Java中两种动态代理的实现

本文介绍了java中两种动态代理的实现方法,Spring的动态代理也是基于这两种方法的.直接附上源码: 1.JDK实现 使用JDK实现动态代理必须使用接口 接口Work.java public interface Work { public void work(); } 实现类WorkImpl.java public class WorkImpl implements Work { @Override public void work() { System.out.println("我在工作&q

SQL中两种表复制语句

Insert是T-sql中常用语句,Insert INTO table(field1,field2,...) values(value1,value2,...)这种形式的在应用程序开发中必不可少.但我们在开发.测试过程中,经常会遇到需要表复制的情况,如将 一个table1的数据的部分字段复制到table2中,或者将整个table1复制到table2中,这时候我们就要使用SELECT INTO 和 INSERT INTO SELECT 表复制语句了. 1.INSERT INTO SELECT语句