2009-05-28 20 views
3

J'essaie d'amener Spring à injecter des mocks EasyMock dans mes tests unitaires.L'utilisation de Spring pour injecter des mocks EasyMock provoque ClassCastException

Dans mon applicationContext.xml, je ceci:

<bean id="mockService" class="org.easymock.EasyMock" factory-method="createMock" name="MockService"> 
    <constructor-arg index="0" value="my.project.Service"/> 
</bean> 

Dans mon test unitaire j'ai ceci:

@Autowired 
@Qualifier("mockService") 
private Service service; 

public void testGetFoo() { 
    Foo foo = new Foo(); 

    expect(service.findFoo()).andReturn(foo); 
    replay(service); // <-- This is line 45, which causes the exception 

    // Assertions go here... 
} 

Lorsque je tente de lancer mon test, je reçois cette trace de la pile:

java.lang.ClassCastException: org.springframework.aop.framework.JdkDynamicAopProxy 
at org.easymock.EasyMock.getControl(EasyMock.java:1330) 
at org.easymock.EasyMock.replay(EasyMock.java:1279) 
at TestFooBar.testGetFoo(TestVodServiceLocator.java:45) 

Je suis tout à fait nouvelle à la fois du printemps et EasyMock, mais il me semble que l'erreur est causée par EasyMock essayant d'appeler un moi thod sur ce qu'il suppose être une instance d'EasyMock, mais est en réalité un proxy dynamique créé par Spring. Si je comprends bien, les proxies dynamiques implémentent uniquement les méthodes définies dans l'interface, dans ce cas l'interface pour Service. Ce que je ne comprends pas, c'est que from what I read (également here), ce que j'essaie de réaliser au moins semble être possible.

Donc, ma question est: Qu'est-ce que je ne fais pas ou que fais-je tort?

Répondre

4

Résolu!

J'avais oublié dans mon applicationContext.xml:

<bean id="txProxyAutoCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
    <property name="beanNames"> 
     <list> 

      <value>*Service</value> 
      <!-- ^^^^^^^^  
       This is the problem! 
      --> 
     </list> 
    </property> 
    <property name="interceptorNames"> 
     <list> 
      <value>txAdvisor</value> 
     </list> 
    </property> 
</bean> 

... ce qui provoque printemps pour créer automatiquement des objets proxy pour tous les haricots avec des noms qui se terminent par « Service ».

Ma solution consistait à lister explicitement les beans au lieu d'utiliser un joker. Cela semble un peu fragile pour moi, cependant, si quelqu'un sait comment spécifier tous les * beans de service sauf FooService, je serais reconnaissant.

1

Quelque chose d'étrange ici. Vous maîtrisez clairement rapidement Spring et EasyMock. Les autoproxies, les méthodes d'usine, tous les bons signes que vous plongez profondément dans les capacités de Spring. Cependant, il est un peu étrange que vous injectiez un simulacre de fèves dans une classe à tous. Vous pouvez avoir une bonne raison, mais pour moi c'est une odeur de code. Je vous conseille de ne considérer que le câblage de services réels dans vos classes de test, puis d'initialiser les objets simulés au besoin. Pourquoi passer 3 lignes en Java et 3 autres lignes en XML (plus 1 ligne pour les réinitialiser) pour créer un objet fantaisie sans dépendances? Dites simplement Service service = (Service) createMock (Service.class). Je conseillerais de les créer dans les méthodes dont vous avez besoin, de définir les attentes, de les injecter, de les utiliser, puis de les supprimer. Dans votre modèle, vous devrez vous rappeler de réinitialiser l'objet fantaisie chaque fois que vous l'utilisez, ce qui est moins clair que de simplement en créer un nouveau.

Bien sûr, il s'agit d'un problème de style et non d'un problème de correction, donc ignorez comme vous le souhaitez.

+0

J'ai simplifié un peu mon exemple avant de le poster, pour le rendre plus simple à comprendre. Le fait est que la classe testée est responsable de l'obtention d'un bean particulier du contexte de l'application au moment de l'exécution en fonction de son nom. Il semblait évident de définir ce bean comme un faux dans le fichier xml. Pourtant, je comprends votre point de vue, et vous avez raison. Je pense avoir mélangé certaines choses et aller un peu trop loin. – KaptajnKold

+0

Cela devient utile si vous voulez intégrer l'exécution de conseils de test ou le câblage. De cette façon, vous pouvez, par exemple, vérifier si les transactions sont correctement appliquées. –

6

Vous pouvez également créer une méthode d'aide pour déballer le proxy EasyMock du proxy de printemps pour définir le comportement attendu alors:

public static <T> T unwrap(T proxiedInstance) { 
    if (proxiedInstance instanceof Advised) { 
    return unwrap((T) ((Advised) proxiedInstance).getTargetSource().getTarget()); 
    } 

    return proxiedInstance; 
} 

Notez l'appel recusive comme dans le pire des cas, vous avez plusieurs proxies enroulé autour de la cible réelle .

+0

J'aime vraiment cette méthode! –

+0

Je voudrais avoir plus de votes upvotes –

1

Je sais que cette question est ancienne, mais je suis tombé sur la recherche d'un problème similaire.

Le problème est que Spring ne connaît pas le type de l'objet fantaisie. La méthode utilisée ressemble à ceci:

public static <T> T createMock(final Class<T> toMock) { 
    return createControl().createMock(toMock); 
} 

Le printemps est pas assez intelligent pour tirer T de l'argument du constructeur (au moins la dernière fois que je l'ai vérifié), il pense que l'objet retourné est de type java.lang.Object . Par conséquent, le proxy créé n'implémente pas my.project.Service et ne peut donc pas être injecté.

La réponse est donc de dire à Spring le type requis.