2010-03-15 5 views
27

J'ai confié à Google Guice la responsabilité du câblage de mes objets. Mais, comment puis-je tester si les liaisons fonctionnent bien.Comment tester les injections de guice?

Par exemple, supposons que nous ayons une classe A qui a une dépendance B. Comment puis-je tester que B est injecté correctement?

class A { 
    private B b; 
    public A() {} 

    @Inject 
    public void setB(B b) { 
     this.b = b 
    } 
} 

Avis A n'a pas obtenu une méthode getB() et je veux affirmer que A.b n'est pas null.

Répondre

40

Pour tout projet Guice complexe, vous devez ajouter des tests pour vous assurer que les modules peuvent être utilisés pour créer vos classes. Dans votre exemple, si B était un type que Guice ne pouvait pas comprendre comment créer, alors Guice ne sera pas capable de créer A. Si A n'était pas nécessaire pour démarrer le serveur, mais était nécessaire lorsque votre serveur gérait un demande, cela causerait des problèmes.

Dans mes projets, j'écris des tests pour des modules non triviaux. Pour chaque module, j'utilise requireBinding() pour déclarer quelles liaisons le module requiert mais ne définit pas. Dans mes tests, je crée un injecteur Guice en utilisant le module testé et un autre module qui fournit les liaisons requises. Voici un exemple utilisant JUnit4 et JMock:

/** Module that provides LoginService */ 
public class LoginServiceModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    requireBinding(UserDao.class); 
    } 

    @Provides 
    LoginService provideLoginService(UserDao dao) { 
    ... 
    } 
} 

@RunWith(JMock.class) 
public class LoginServiceModuleTest { 
    private final Mockery context = new Mockery(); 

    @Test 
    public void testModule() { 
    Injector injector = Guice.createInjector(
     new LoginServiceModule(), new ModuleDeps()); 

    // next line will throw an exception if dependencies missing 
    injector.getProvider(LoginService.class); 
    } 

    private class ModuleDeps extends AbstractModule { 
    private final UserDao fakeUserDao; 

    public ModuleDeps() { 
     fakeUserDao = context.mock(UserDao.class); 
    } 

    @Override 
    protected void configure() {} 

    @Provides 
    Server provideUserDao() { 
     return fakeUserDao; 
    } 
    } 
} 

Notez que le test ne demande qu'un fournisseur. C'est suffisant pour déterminer que Guice pourrait résoudre les liaisons. Si LoginService a été créé par une méthode fournisseur, ce test ne testera pas le code dans la méthode fournisseur.

Ce test ne vérifie pas non plus que vous avez lié la bonne chose à UserDao, ou que UserDao a été correctement défini. Certains diront que ce genre de choses vaut rarement la peine d'être vérifié; S'il y a un problème, cela arrive une fois. Vous devriez "tester jusqu'à ce que la peur se transforme en ennui". Je trouve les tests Module utiles parce que j'ajoute souvent de nouveaux points d'injection, et il est facile d'oublier d'ajouter une liaison.

Les appels requireBinding() peuvent aider Guice à intercepter les liaisons manquantes avant qu'il ne renvoie votre injecteur! Dans l'exemple ci-dessus, le test fonctionnerait toujours si les appels requireBinding() n'existaient pas, mais j'aime les avoir parce qu'ils servent de documentation. Pour les modules plus compliqués (comme mon module racine), je peux utiliser Modules.override() pour remplacer les liaisons que je ne veux pas au moment du test (par exemple, si je veux vérifier que mon objet racine doit être créé, je vais probablement ne le veux pas créer un objet qui se connectera à la base de données). Pour les projets simples, vous pouvez uniquement tester le module de niveau supérieur.

Notez que Guice will not inject nulls à moins que le champ ne soit annoté avec @Nullable de sorte que vous ayez très rarement besoin de vérifier que les objets injectés sont non nuls dans vos tests.En fait, quand j'annote des constructeurs avec @Inject je ne cherche pas à vérifier si les paramètres sont null (en fait, mes tests injectent souvent null dans le constructeur pour garder les tests simples).

+0

Merci beaucoup. Vos arguments sont très clairs. J'ai des dépendances qui ne sont pas créées après que l'application a été déployée – yeraycaballero

+1

'mes tests injectent souvent une valeur nulle dans le constructeur pour garder les tests simples' => cela peut indiquer que votre classe n'est pas aussi cohérente – beluchin

+0

@beluchin pourriez-vous expliquer ce que vous voulez dire? J'essaie d'éviter de faire du «vrai travail» dans mon constructeur, donc passer dans un «null» dans le constructeur est rarement un problème. Si je teste une classe, et que la ou les méthodes que je teste n'utilisent pas l'un des champs, le fait de passer un 'null' pour ce paramètre constructeur est la chose la plus simple à faire. Si cela ne marche pas, j'injecte un objet simulé (pour les services) ou une instance réelle (pour les objets de valeur) mais l'un ou l'autre rend le code plus compliqué que si je passais juste dans 'null' – NamshubWriter

-1

Je ne pense pas que vous devriez tester les membres privés étant mis en place. Mieux vaut tester contre l'interface publique de votre classe. Si le membre "b" ne serait pas injecté, vous obtiendrez probablement une exception NullPointerException en train d'exécuter vos tests, ce qui devrait être suffisant.

1

À mon humble avis, vous ne devriez pas tester cela. Les gars de Google Guice ont les tests unitaires pour affirmer que les injections fonctionnent comme prévu - après tout, c'est ce que Guice est conçu pour faire. Vous devriez seulement écrire des tests pour votre propre code (A et B).

+2

Je pense que vous pourriez avoir besoin de comprendre DI un peu plus. Le fait est que le cadre de DI «résultera» en instances construites et les injectera pour vous si nécessaire (c'est pourquoi elles ont été créées) - vous séparerez votre logique de création et de gestion. Maintenant, vous pouvez les tester séparément. Tester votre code de création, cependant, ne signifie pas essayer de s'assurer que Guice fonctionne comme prévu. Si vous ne faites pas confiance à la bibliothèque, vous devriez peut-être utiliser autre chose. – gpampara

+12

Les gars de Guice ne sont pas d'accord avec votre déclaration. Citation: "Si vos fournisseurs sont complexes, assurez-vous de les tester!" (http://code.google.com/p/google-guice/wiki/ProviderBindings) – Joe23

+4

@ Joe23 - tester les fournisseurs Guice et tester les liaisons Guice sont deux choses différentes. – topchef

3

Une autre façon de tester votre configuration est de disposer d'une suite de tests qui teste votre application de bout en bout. Bien que les tests de bout en bout testent nominalement les cas d'utilisation, ils vérifient indirectement que votre application est configurée correctement (que toutes les dépendances sont câblées, etc.). D'autre part, les tests unitaires doivent se concentrer exclusivement sur le domaine, et non sur le contexte dans lequel votre code est déployé. Je suis également d'accord avec la réponse de NamshubWriter. Je ne suis pas contre les tests qui vérifient la configuration tant qu'ils sont regroupés dans une suite de tests séparée pour vos tests unitaires.