Unité-le test d'une classe qui appelle une méthode statique
Je suis en train de test unitaire d'une classe 'A' qui appelle une méthode statique d'une classe "B". Classe " B " est essentiellement un google goyave cache qui récupère une valeur(Objet) à partir du cache de donner une clé, ou des charges de l'objet dans le cache (dans le cas d'un cache-miss) à l'aide d'un adaptateur de service. Le service de l'adaptateur de classe a d'autres autocâblés dépendances pour récupérer l'objet.
Ce sont les classes à des fins d'illustration:
Classe Un
public class A {
public Object getCachedObject(String key) {
return B.getObjectFromCache(key);
}
}
Classe B
public class B {
private ServiceAdapter serviceAdapter;
public void setServiceAdapter(ServiceAdapter serAdapt) {
serviceAdapter = serAdapt;
}
private static final LoadingCache<String, Object> CACHE = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(30, TimeUnit.MINUTES)
.build(new MyCacheLoader());
public static Object getObjectFromCache(final String key) throws ExecutionException {
return CACHE.get(warehouseId);
}
private static class MyCacheLoader extends CacheLoader<String, Object> {
@Override
public Object load(final String key) throws Exception {
return serviceAdapter.getFromService(key)
}
}
}
Service De L'Adaptateur Classe
public class ServiceAdapter {
@Autowired
private MainService mainService
public Object getFromService(String key) {
return mainService.getTheObject(key);
}
}
Je suis capable de faire le test d'intégration avec succès et fetch (ou de charge) la valeur de (ou dans) le cache. Cependant, je suis incapable d'écrire les tests unitaires pour la classe A. C'est ce que j'ai essayé:
Unité de Test pour la Classe A
@RunWith(EasyMocker.class)
public class ATest {
private final static String key = "abc";
@TestSubject
private A classUnderTest = new A();
@Test
public void getCachedObject_Success() throws Exception {
B.setServiceAdapter(new ServiceAdapter());
Object expectedResponse = createExpectedResponse(); //some private method
expect(B.getObjectFromCache(key)).andReturn(expectedResponse).once();
Object actualResponse = classUnderTest.getCachedObject(key);
assertEquals(expectedResponse, actualResponse);
}
}
Quand je lance le test unitaire, il échoue avec une NullPointerException à ServiceAdapter classe où l'appel: mainService.getTheObject(key) est fabriqué.
Comment se moquer de la dépendance de ServiceAdapter alors que les tests unitaires de la classe A. je ne devrais pas être simplement préoccupés par la dépendance immédiate que la classe A a, viz. B.
Je suis sûr que je suis en train de faire quelque chose de fondamentalement mauvais. Comment dois-je écrire les tests unitaires de la classe A?
B
n'a même pas compilerJe voulais juste illustrer la façon dont les classes sont comme. J'ai une abstraction de beaucoup juste pour des fins d'illustration, et ce n'est pas la véritable cours en tant que tels.
En règle générale, si vous avez de se moquer d'un appel à une méthode statique, cela signifie que votre code n'est pas correctement écrit et doit être réécrit pour être testable, il est donc important que vous donnez quelque chose de travailler si vous voulez savoir comment vous devez réécrire votre code
Injecter de la classe de cache comme une dépendance au lieu de le rendre statique.
OriginalL'auteur user1639485 | 2016-10-07
Vous devez vous connecter pour publier un commentaire.
Vous savez maintenant pourquoi la méthode statique sont réputées mauvaises pratiques pour les tests unitaires,
comme ils font en se moquant presque impossible, esp. si ils sont dynamique.
Il est donc plus pratique de refactoriser B
static
les méthodes dans un ensemble de non-static public.De catégorie A doit obtenir une instance de la classe B de l'injection, soit par le constructeur ou l'injection par mutateur. Dans Votre ATest vous ensuite instancier la classe A avec une maquette de la classe B, et de l'avoir de retour de ce que vous voulez en fonction de votre cas de test et de base vos affirmations.
Ce faisant, vous vraiment tester la unité qui, à terme, devrait être l'interface publique de la classe A. (C'est aussi pourquoi j'aime pour une classe d'avoir une seule méthode publique dans un monde idéal.)
En ce qui concerne votre exemple précis: La maquette de B devrait également soucie pas de ses propres dépendances. Actuellement vous écrivez dans votre test:
Vous êtes dans
ATest
. Pas dansBTest
.ATest
ne devrait avoir qu'une se moquer de deB
, afin de passer une instance de laServiceAdapter
ne devrait pas être nécessaire.Vous devez soin de la manière dont Un public de méthodes se comporte, et qui peut changer compte tenu de certaines réponses de B méthodes publiques.
Ce que je trouve bizarre, c'est que la méthode que vous voulez tester essentiellement qu'un wrapper pour B. Peut-être que cela a un sens dans votre cas encore cette également des conseils pour moi, peut-être que vous voulez déjà injecter un
Object
en place d'une instance de B.Si vous ne voulez pas de se perdre en se moquant de l'enfer, il aide vraiment à avoir le moins d'méthodes publiques par classe qui à son tour avoir le moins de dépendances que possible. Je cherche à trois dépendances par classe, et permet jusqu'à cinq occasions spéciales. (Chaque dépendance peut avoir des répercussions énormes sur les moqueries de frais généraux.)
Si vous avez trop de dépendances, certes, certaines parties peuvent être déplacés vers d'autres/de nouveaux services.
Point valide. J'ajouterai que peu.
OriginalL'auteur k0pernikus
La ré-écriture du code pour le rendre plus testable a déjà été expliqué dans une autre réponse. Parfois, il est difficile d'éviter ces cas.
Si vous avez vraiment envie de se moquer d'un appel statique, vous pouvez utiliser PowerMock. Vous aurez besoin d'utiliser @PrepareForTest({CACHE.class}) annotation pour votre classe, suivi par le code ci-dessous dans l'unité de test.
OriginalL'auteur Andy
Afin d'obtenir autour de ce que vous pourriez enchaîner un interfacé type de référentiel de classe autour de la classe B. une Fois que vous disposez d'une interface, vous pouvez alors stub pour des fins de test.
En faisant cela, vous isoler Un à partir des rouages de B et de se concentrer uniquement sur les actions résultant de B (je suppose que c'est juste une autre façon de dire au programme d'une interface plutôt qu'une classe de béton)
OriginalL'auteur Gavin