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:
- Mit
EasyMock.<Void>expect
. Dies hat zu einem compiler-Fehler. -
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)
-
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?
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
Du musst angemeldet sein, um einen Kommentar abzugeben.
Da keine der Klassen sind final, die Sie verwenden können, "pure mockito" ohne Rückgriff auf PowerMockito:
Beachten Sie, dass die Methode "Argumente" (stub) sind in der Tat argument Matcher; Sie können spezifische Werte (falls nicht "umgeben" von einer bestimmten Methode wird es einen Aufruf
.equals()
). So können Sie die stub-Verhalten-unterschiedlich für verschiedene Argumente.Auch, keine Notwendigkeit für jede Art von
.replay()
mit Mockito, das ist sehr nett!Schließlich, bewusst sein, dass Sie
doCallRealMethod()
als gut. Danach hängt es davon ab, Ihre Szenarien...(Hinweis: Letzte mockito-version auf maven ist 1.10.17 FWIW)
InformationsquelleAutor fge
Sind Sie mit EasyMock oder Mockito? Beide sind mit unterschiedlichen Rahmenbedingungen.
PowerMockito ist eine Obermenge (oder eher eine Ergänzung), die verwendet werden können mit diesen beiden frameworks. PowerMockito ermöglicht es Ihnen, Dinge zu tun, Mockito oder EasyMock nicht.
Versuchen, diese für stubbing void-Methoden zum auslösen von Ausnahmen:
EasyMock:
Check:
Mockito:
InformationsquelleAutor nhylated
Verwenden
expectLastCall
, wie:Dies wirft
java.lang.IllegalStateException: no last call on a mock available
. Ich TesteSomeClient#getEntity
, nichtCacheWrapper
.OK, habe ich missverstanden; so, du meinst, zu machen .getEntity() eine exception werfen und fangen?
das ist richtig.
InformationsquelleAutor NiematojakTomasz