2010-09-07 5 views
43

Mon code:ressort 3 autowiring et les tests JUnit

@Component 
public class A { 
    @Autowired 
    private B b; 

    public void method() {} 
} 

public interface X {...} 

@Component 
public class B implements X { 
    ... 
} 

Je veux tester en classe d'isolement A. Est-ce que je dois à la classe B maquette? Si oui, comment? Parce qu'il est autowired et il n'y a pas setter où je pourrais envoyer l'objet mocké.

Répondre

81

Je veux tester en classe d'isolement A.

Vous devriez B absolument faux, plutôt que instancier et injecter une instance de B. Le point est de tester si A ou B ne travaille pas, donc vous ne devriez pas permettre à un B potentiellement brouillé d'interférer avec le test de A.

Cela dit, je recommande fortement Mockito. Comme les cadres moqueurs, il est extrêmement facile à utiliser. Vous écrivez quelque chose comme ce qui suit:

@Test 
public void testA() { 
    A a = new A(); 
    B b = Mockito.mock(B.class); // create a mock of B 
    Mockito.when(b.getMeaningOfLife()).thenReturn(42); // define mocked behavior of b 
    ReflectionTestUtils.setField(a, "b", b); // inject b into the B attribute of A 

    a.method(); 

    // call whatever asserts you need here 
} 
+32

+1 pour se moquer de getMeaningOfLife() à 42 :-). – Dave

+11

Avec la nouvelle version de Mockito, j'utiliserais l'annotation '@ InjectMocks' sur la déclaration de' A' et me débarrasserais de la réflexion 'setField (..)' – Snekse

+0

Mais a est un bean, c'est-à-dire un proxy (créé avec AOP). Cela ne réussira pas. J'ai essayé quelque chose de similaire et l'erreur était que le champ b ne pouvait pas être trouvé sur A (parce que c'est un proxy). –

15

Vous pouvez injecter le champ par réflexion en utilisant le ReflectionTestUtils.setField de Spring (ou l'extension junit PrivateAccessor) ou vous pouvez créer un contexte d'application fictif et le charger. Bien que pour un simple test d'unité (non-intégration), je préfère utiliser la réflexion pour plus de simplicité.

0

This forum discussion est logique pour moi. Vous pouvez déclarer votre membre privé b comme un type d'InterfaceB qui est implémenté par la classe B (ie: orientée service) puis déclarer qu'une classe MockB implémenterait également la même interface. Dans votre contexte d'application d'environnement de test, vous déclarez la classe MockB et le contexte de votre application de production, vous déclarez la classe B normale et, dans les deux cas, le code de la classe A n'a pas besoin d'être modifié.

+1

Avec @Autowire vous ne déclarez pas vos haricots dans le contexte de l'application, ils sont juste ici dans le classpath. Donc, cette solution ne fonctionne pas. – Damien

+0

Cette solution fonctionne si vous câblez le bean byType (en 2.5) ou déclarez un qualificatif (3.0). Au printemps 2.5, vous pouvez autowire à un bean déclaré dans votre configuration XML Application Context comme peut être lu dans la documentation http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory- autowire. Au printemps 3.0, ils vous permettent encore de le faire avec l'annotation @Qualifier (documentée à http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans- autowired-annotation). – Aaron

18

Voici un exemple de la façon dont je suis arrivé mes tests travailler avec Spring 3.1, JUnit 4.7 et 1.9 Mockito:

FooService.java

public class FooService { 
    @Autowired private FooDAO fooDAO; 
    public Foo find(Long id) { 
     return fooDAO.findById(id); 
    } 
} 

FooDAO.java

public class FooDAO { 
    public Foo findById(Long id) { 
     /* implementation */ 
    } 
} 

FooServiceTest.java

@RunWith(MockitoJUnitRunner.class) 
public class FooServiceTest { 
    @Mock private FooDAO mockFooDAO; 
    @InjectMocks private FooService fooService = new FooService(); 

    @Test public final void findAll() { 
     Foo foo = new Foo(1L); 
     when(mockFooDAO.findById(foo.getId()).thenReturn(foo); 

     Foo found = fooService.findById(foo.getId()); 
     assertEquals(foo, found); 
    } 
} 
+6

Si ne pas utiliser 'MockitoJUnitRunner', important de se rappeler dans' FooServiceTest': \t '@Before \t initMocks public void() { \t \t MockitoAnnotations.initMocks (this); \t} ' – TalkLittle

Questions connexes