Mockito – 监视真实对象调用原始方法

想象一下以下代码:

List list = ..... List spy = spy(list); doThrow(new NullpointerException()).when(spy).get(0); 

doThrow(....)执行list.get(0) – 这根本没有意义。 我想定义模拟行为,而不是在这里调用方法…..我错过了什么?

编辑:列表由CGLIB装饰。 当我删除CGLIB代理时,Mockito按预期工作。 任何想法在使用CGLIB代理时如何解决这样的问题?

谢谢,Maciej

 import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import java.lang.reflect.Method; import org.junit.Test; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MockitoSpyTest { @Test public void execTest() { System.out.println("*** TEST 1 ***"); System.out.println("Test on unmodified object"); MySet ms = new MySetImpl(); ms.set("test value"); System.out.println("Set contains: " + ms.get()); // decorate ms1 with easymock System.out.println("\n*** TEST 2 ***"); MySet spyMs = spy(ms); doThrow(new NullPointerException("my test nullpointer")).when(spyMs).get(); System.out.println("Test decorated object with SPY"); spyMs.set("test value"); try { System.out.println("Set contains: " + spyMs.get()); } catch (NullPointerException e) { System.out.println("NullPointerException - as expected"); } // Enhance call with CGLIB System.out.println("\n*** TEST 3 ***"); System.out.println("Test on CGLIB decorated object"); Enhancer enc = new Enhancer(); enc.setSuperclass(MySetImpl.class); enc.setInterfaces(new Class[] { MySet.class }); enc.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { if ("get".equals(method.getName())) { System.out.println("CGLIB decorated GET call"); } return proxy.invokeSuper(obj, args); } }); MySet ms1 = (MySet) enc.create(); ms1.set("test value"); System.out.println("Set contains: " + ms1.get()); // decorate ms1 with easymock System.out.println("\n*** TEST 4 ***"); System.out.println("Test on CGLIB decorated object with SPY"); MySet spyMs1 = spy(ms1); doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get(); spyMs1.set("test value"); System.out.println("Set contains: " + spyMs1.get()); } public interface MySet { void set(String val); String get(); } public static class MySetImpl implements MySet { String val; public void set(String val) { this.val = val; System.out.println("Original SET call:" + val); } public String get() { System.out.println("Original GET call:" + val); return val; } } } 

上面的例子产生输出:

 *** TEST 1 *** Test on unmodified object Original SET call:test value Original GET call:test value Set contains: test value *** TEST 2 *** Test decorated object with SPY Original SET call:test value NullPointerException - as expected *** TEST 3 *** Test on CGLIB decorated object Original SET call:test value CGLIB decorated GET call Original GET call:test value Set contains: test value *** TEST 4 *** Test on CGLIB decorated object with SPY CGLIB decorated GET call Original GET call:test value Original SET call:test value CGLIB decorated GET call Original GET call:test value Set contains: test value 

现在TEST 2TEST 4应该在get调用时抛出NullPointerException – 基于mockito spy: doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get();

“TEST 4”没有抛出预期的exception,因为它已经用CGLIB修饰了 – 我们也可以在控制台上看到正在执行CGLIb调用: GLIB decorated GET call而没有调用spy对象。 当使用带有CGLIB代理的Spring AOP时,可以获得相同的效果。

 Mockito.doThrow(new NullpointerException()).when(spy).get(0); 

我认为这里的问题是你正在尝试进行局部模拟,所以你必须在测试类上有注释:

 @PrepareForTest(List.class) 

这可能有效,也可能无效。 看看我测试exception处理的代码,我总是在完全模拟的对象上完成它,而不是部分模拟的对象。 此外,我在部分模拟时广泛使用了PowerMockito,因此库可能会满足您的需求。