run - spring 3 autowiring y junit testing
test java spring (4)
Mi código:
@Component
public class A {
@Autowired
private B b;
public void method() {}
}
public interface X {...}
@Component
public class B implements X {
...
}
Quiero probar en aislamiento clase A. ¿Tengo que burlarme de la clase B? Si es así, ¿cómo? Porque está auto-conectado y no hay un setter donde pueda enviar el objeto burlado.
Quiero probar en aislamiento clase A.
Deberías burlar B en lugar de instanciar e inyectar una instancia de B. El punto es probar A si B funciona o no, por lo que no debes permitir que un B potencialmente roto interfiera con la prueba de A.
Dicho eso, recomiendo Mockito . Como van los marcos burlones, es extremadamente fácil de usar. Escribirías algo como lo siguiente:
@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
}
Aquí hay un ejemplo de cómo hice que mis pruebas funcionaran con Spring 3.1, JUnit 4.7 y 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);
}
}
Puede inyectar el campo a través de la reflexión utilizando Spring''s ReflectionTestUtils.setField
(o la extensión PrivateAccessor
) o puede crear un contexto de aplicación simulada y cargar eso. Aunque para una prueba simple de unidad (no integración), prefiero usar la reflexión para simplificar.
Esta discusión en el foro tiene sentido para mí. Puede declarar su miembro privado b como un tipo de InterfaceB que es implementado por la clase B (es decir, orientado al servicio) y luego declarar que una clase MockB también implementaría la misma interfaz. En el contexto de la aplicación del entorno de prueba, declaras la clase MockB y el contexto de tu aplicación de producción declaras la clase B normal y, en cualquier caso, el código para la clase A no necesita cambiarse ya que se autoconectará.