Mockito - spying on real objects calls original method

后端 未结 2 892
情书的邮戳
情书的邮戳 2021-02-18 20:32

Imagine following code:

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

doThrow(....)

相关标签:
2条回答
  • 2021-02-18 21:12
    Mockito.doThrow(new NullpointerException()).when(spy).get(0);
    

    I think the problem here is that you are trying to do a partial mock and so you have to have the annotation on your test class:

    @PrepareForTest(List.class)
    

    This may or may not work. Looking at my code where I test exception handling, I always have done it on a fully-mocked object, not a partially mocked one. Also, I have made extensive use of PowerMockito when partial mocking, so it is possible that library will do what you need.

    0 讨论(0)
  • 2021-02-18 21:30
    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;
            }
    
        }
    }
    

    Example above produces output:

    *** 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
    

    Now the TEST 2 and TEST 4 should throw NullPointerException on get call - based on mockito spy: doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get();

    The "TEST 4" does not throw expected exception because it is already decorated with CGLIB - we can also see on the console that CGLIb call is being executed: GLIB decorated GET call and not call on spy object. The same effect can be achived when using Spring AOP with CGLIB proxies.

    0 讨论(0)
提交回复
热议问题