Wie kann ich simulieren Sie eine void-Methode eine exception werfen?

Habe ich eine Struktur wie diese:

public class CacheWrapper {
    private Map<Object, Object> innerMap;

    public CacheWrapper() {
        //initialize the innerMap with an instance for an in-memory cache
        //that works on external server
        //current implementation is not relevant for the problem
        innerMap = ...;
    }

    public void putInSharedMemory(Object key, Object value) {
        innerMap.put(key, value);
    }

    public Object getFromSharedMemory(Object key) {
        return innerMap.get(key);
    }
}

Und meine client-Klasse (man könnte sagen es sieht so aus):

public class SomeClient {
    //Logger here, for exception handling
    Logger log = ...;
    private CacheWrapper cacheWrapper;
    //getter and setter for cacheWrapper...

    public Entity getEntity(String param) {
        Entity someEntity = null;
        try {
            try {
                entity = cacheWrapper.getFromSharedMemory(param);
            } catch (Exception e) {
                //probably connection failure occurred here
                log.warn("There was a problem when getting from in-memory " + param + " key.", e);
            }
            if (entity == null) {
                entity = ...; //retrieve it from database
                //store in in-memory cache
                try {
                    cacheWrapper.put(param, entity);
                } catch (Exception e) {
                    //probably connection failure occurred here
                    log.warn("There was a problem when putting in in-memory " + param + " key.", e);
                }
            }
        } catch (Exception e) {
            logger.error(".......", e);
        }
        return entity;
    }
}

Ich bin erstellen von unit-tests für SomeClient#getEntity Methode und decken alle Szenarien. Zum Beispiel, ich brauche, um zu decken das Szenario, wo gibt es Ausnahmen, die durch cacheWrapper. Der Ansatz, den ich verfolge ist, erstellen Sie ein mock für CacheWrapper Klasse, stellen Sie die Methoden auf CacheWrapper Klasse zu werfen RuntimeException, setzen Sie dieses Modell in eine Instanz von SomeClient - und test -Someclient#getEntity. Das problem ist, wenn Sie versuchen, zu verspotten putInSharedMemory Methode, da ist void. Ich habe versucht, viele Möglichkeiten, dies zu tun, aber keiner von Ihnen arbeiten. Das Projekt hat Abhängigkeiten für PowerMock und EasyMock.

Hier sind meine versuche:

  1. Mit EasyMock.<Void>expect. Dies hat zu einem compiler-Fehler.
  2. Versucht, stub CacheWrapper#putInSharedMemory. Hat nicht geklappt, da erhob eine exception mit dieser Fehlermeldung:

    java.lang.AssertionError: Unerwarteter Aufruf der Methode putInSharedMemory("foo", com.company.domain.Entity@609fc98)

  3. Hinzugefügt Mockito Abhängigkeit zum Projekt zu verwenden, die die Funktionalität von PowerMockito Klasse. Aber das warf eine Ausnahme, weil Sie sich nicht integrieren, mit EasyMock. Dies ist die Ausnahme ausgelöst:

    java.lang.Classcastexception-Fehler: org.powermock.api.easymock.intern.invocationcontrol.EasyMockMethodInvocationControl kann nicht umgewandelt werden, org.powermock.api.mockito.intern.invocationcontrol.MockitoMethodInvocationControl

Hier ist der code für dieses unit-test-Beispiel:

@Test
public void getEntityWithCacheWrapperException() {
    CacheWrapper cacheWrapper = mockThrowsException();
    SomeClient someClient = new SomeClient();
    someClient.setCacheWrapper(cacheWrapper);
    Entity entity = someClient.getEntity();
    //here.....................^
    //cacheWrapper.putInSharedMemory should throw an exception

    //start asserting here...
}

//...

public CacheWrapper mockThrowsException() {
    CacheWrapper cacheWrapper = PowerMock.createMock(CacheWrapper.class);
    //mocking getFromSharedMemory method
    //this works like a charm
    EasyMock.expect(cacheWrapper.getFromSharedMemory(EasyMock.anyObject()))
        .andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();

    //mocking putInSharedMemory method
    //the pieces of code here were not executed at the same time
    //instead they were commented and choose one approach after another

    //attempt 1: compiler exception: <Void> is not applicable for <void>
    EasyMock.<Void>expect(cacheWrapper.putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject()))
        .andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();

    //attempt 2: stubbing the method
    //exception when executing the test:
     //Unexpected method call putInSharedMemory("foo", com.company.domain.Entity@609fc98)
    Method method = PowerMock.method(CacheWrapper.class, "putInSharedMemory", Object.class, Object.class);
    PowerMock.stub(method).toThrow(new RuntimeException("Exception on purpose."));

    //attempt 3: added dependency to Mockito integrated to PowerMock
    //bad idea: the mock created by PowerMock.createMock() belongs to EasyMock, not to Mockito
    //so it breaks when performing the when method
    //exception:
    //java.lang.ClassCastException: org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl
    //cannot be cast to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
    //at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:54)
    PowerMockito.doThrow(new RuntimeException("Exception on purpose."))
        .when(cacheWrapper).putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject());

    PowerMock.replay(cacheWrapper);
    return cacheWrapper;
}

Kann ich nicht ändern Sie die Implementierung von CacheWrapper denn Sie kommt von einem Drittanbieter-Bibliothek. Ich kann auch nicht verwenden EasyMock#getLastCall weil ich die Durchführung der Prüfung auf SomeClient#getEntity.

Wie kann ich das umgehen?

Warum brauchen Sie PowerMockito überhaupt? Keines der getesteten Klassen sind final, Sie könnten nur mit einem spy() über instantiierte Instanz... (kann stub-Spione). Auch nicht, dass, wo .replay() kommt?
Ich bin nicht sehr geschickt mit diesen Rahmenbedingungen, weil ich Neige dazu, zu schreiben, integration tests eher als Reine unit-tests. Könnten Sie bitte erweitern Sie mehr über dieses spy() Methode oder einem entsprechenden doc?
Alles in allem ist das testen von code ist wirklich Bizarr, du scheinst mit beiden easymock und (macht -) mockito... irgendeinen Grund, warum?
Hinzugefügt powermockito nur weil es eine andere Lösung, aber wie bereits in Versuch 3, es ist eine schlechte Idee 🙂
Nun, zum Beispiel, mit reinem mockito, die Sie tun können final Foo foo = spy(new Foo()); doThrow(something).when(foo).someMethod(blah)

InformationsquelleAutor Luiggi Mendoza | 2015-01-09

Schreibe einen Kommentar