Comment puis-je simuler une méthode void lancer une exception?
J'ai une structure comme ceci:
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);
}
}
Et ma classe client (on pourrait dire qu'il ressemble à ça):
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;
}
}
Je suis la création de tests unitaires pour SomeClient#getEntity
méthode pour couvrir tous les scénarios. Par exemple, j'ai besoin de couvrir le scénario où il y a des exceptions levées par cacheWrapper
. L'approche que je suis est de créer une maquette pour CacheWrapper
classe, rendre les méthodes sur CacheWrapper
classe de jeter un RuntimeException
, définissez cette fantaisie dans une instance de SomeClient
et test Someclient#getEntity
. Le problème est alors d'essayer de se moquer de putInSharedMemory
méthode, car il est void
. J'ai essayé beaucoup de façons de le faire, mais aucune de ces travaux. Le projet a des dépendances pour PowerMock et EasyMock.
Voici mes tentatives:
- À l'aide de
EasyMock.<Void>expect
. Cela a soulevé une erreur du compilateur. -
Essayé de stub
CacheWrapper#putInSharedMemory
. N'a pas fonctionné, parce qu'a soulevé une exception avec ce message d'erreur:java.lang.AssertionError: Inattendu appel de la méthode putInSharedMemory("foo", com.company.domain.Entity@609fc98)
-
Ajouté Mockito dépendance au projet de faire usage de la fonctionnalité de
PowerMockito
classe. Mais cela a soulevé une exception, car il ne s'intègre pas avec EasyMock. C'est l'exception soulevée:java.lang.ClassCastException: org.powermock.l'api.easymock.interne.invocationcontrol.EasyMockMethodInvocationControl ne peut pas être lancé pour org.powermock.l'api.mockito.interne.invocationcontrol.MockitoMethodInvocationControl
Voici le code de cette unité de l'échantillon:
@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;
}
Je ne peux pas changer la mise en œuvre de CacheWrapper
parce qu'il provient d'un tiers de la bibliothèque. Aussi, je ne peux pas utiliser EasyMock#getLastCall
parce que je suis l'exécution de l'essai sur SomeClient#getEntity
.
Comment puis-je surmonter cela?
spy()
sur une instancié exemple... (vous stub espions). Aussi, où est-ce que .replay() vient-il?Je ne suis pas très habile à l'aide de l'un de ces cadres, parce que j'ai tendance à écrire des tests d'intégration plutôt que la pure tests unitaires. S'il vous plaît pourriez-vous développer davantage sur ce
spy()
méthode ou toute autre doc?Dans l'ensemble le code de test est vraiment bizarre, vous semblez être en utilisant à la fois easymock et (pouvoir)mockito... quelle en est la raison?
ajouté powermockito juste parce qu'il a offert une autre solution, mais comme noté dans la tentative 3, c'est une mauvaise idée 🙂
Ainsi, par exemple, avec de la pure mockito, vous pouvez le faire
final Foo foo = spy(new Foo()); doThrow(something).when(foo).someMethod(blah)
OriginalL'auteur Luiggi Mendoza | 2015-01-09
Vous devez vous connecter pour publier un commentaire.
Puisque aucun de vos classes sont définitives, vous pouvez utiliser "pure mockito" sans recourir à PowerMockito:
Note que "les arguments de méthode" à un talon sont, en fait, l'argument de rapprochement; vous pouvez mettre des valeurs (si ce n'est "entouré" par une méthode spécifique, il fera un appel à
.equals()
). Donc, vous pouvez guider le stub du comportement différemment pour les différents arguments.Aussi, pas besoin de tout type de
.replay()
avec Mockito, qui est très agréable!Enfin, sachez que vous pouvez
doCallRealMethod()
. Après, tout dépend de vos scénarios...(remarque: la dernière mockito version disponible sur maven est 1.10.17 FWIW)
OriginalL'auteur fge
Êtes-vous à l'aide de EasyMock ou Mockito? Les deux sont différents cadres.
PowerMockito est un sur-ensemble (ou un complément) qui peuvent être utilisés avec ces deux cadres. PowerMockito vous permet de faire des choses qui Mockito ou EasyMock ne pas.
Essayez ceci pour cogner les méthodes void lancer des exceptions:
EasyMock:
Vérifier:
Mockito:
OriginalL'auteur nhylated
Utilisation
expectLastCall
, comme:Ce lève
java.lang.IllegalStateException: no last call on a mock available
. Je suis en essaisSomeClient#getEntity
, pasCacheWrapper
.OK, j'ai mal compris; c'est donc vous dire de faire .getEntity() lève une exception et attraper?
c'est le droit.
OriginalL'auteur NiematojakTomasz