Mon code:
@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. Dois-je me moquer de la classe B? Si oui, comment? Parce qu'il est câblé automatiquement et qu'il n'y a pas de setter où je pourrais envoyer l'objet moqué.
Je veux tester en classe d'isolement A.
Vous devez absolument vous moquer de B, plutôt que d'instancier et d'injecter une instance de B. Le but est de tester A si B fonctionne ou non, donc vous ne devez pas permettre à un B potentiellement cassé d'interférer avec le test de A.
Cela dit, je recommande fortement Mockito . Au fur et à mesure que les frameworks se moquent, il est extrêmement facile à utiliser. Vous écririez quelque chose comme ceci:
@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
}
Voici un exemple de la façon dont mes tests ont fonctionné avec Spring 3.1, JUnit 4.7 et Mockito 1.9:
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);
}
}
Vous pouvez injecter le champ via la réflexion en utilisant le ReflectionTestUtils.setField
De Spring (ou l'extension junit PrivateAccessor
) ou vous pouvez créer un contexte d'application simulé et le charger. Bien que pour un test unitaire simple (non-intégration), je préfère utiliser la réflexion pour plus de simplicité.
Cette discussion sur le forum 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 (c'est-à-dire: orienté 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 votre contexte d'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é car il sera câblé automatiquement.