web-dev-qa-db-fra.com

Mockito - espionner des objets réels appelle la méthode originale

Imaginez le code suivant:

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

doThrow(....) exécute list.get(0) - cela n'a aucun sens. Je voudrais définir le comportement factice et ne pas appeler une méthode ici ..... quelque chose me manque-t-il?

EDIT: La liste est décorée par CGLIB. Lorsque je supprime le proxy CGLIB, Mockito fonctionne comme prévu. Une idée de comment résoudre ce problème lors de l’utilisation des proxies CGLIB? 

19
Maciej Miklas
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;
        }

    }
}

L'exemple ci-dessus produit une sortie:

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

Maintenant, le TEST 2 et le TEST 4 devraient lancer NullPointerException sur un appel get - basé sur l'espion mockito: doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get();

Le "TEST 4" ne lance pas l'exception attendue car il est déjà décoré avec CGLIB - nous pouvons également voir sur la console que l'appel CGLIb est en cours d'exécution: GLIB decorated GET call et ne pas appeler d'objet espion. Le même effet peut être obtenu lorsque vous utilisez Spring AOP avec des mandataires CGLIB.

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

Je pense que le problème ici est que vous essayez de faire une simulation partielle et que vous devez donc avoir l'annotation sur votre classe de test:

@PrepareForTest(List.class)

Cela peut ou peut ne pas fonctionner. En examinant mon code où je teste la gestion des exceptions, je l’ai toujours fait sur un objet totalement fictif, et non partiellement fictif. De plus, j’ai beaucoup utilisé PowerMockito lorsqu’il s’est moqué, il est donc possible que la bibliothèque fasse tout ce dont vous avez besoin.

0
user785262