tutorial mvc mediante libro framework español ejemplos desarrollo books aplicaciones java spring unit-testing junit autowired

java - mvc - Inyectando campo privado @Autowired durante la prueba



spring java tutorial (6)

A veces puede refactorizar su @Component para usar la inyección basada en el constructor o el instalador para configurar su caso de prueba (puede y aún puede confiar en @Autowired ). Ahora, puede crear su prueba completamente sin un marco de burla mediante la implementación de pruebas en su lugar (por ejemplo, MailServiceStub Martin Fowler):

@Component public class MyLauncher { private MyService myService; @Autowired MyLauncher(MyService myService) { this.myService = myService; } // other methods } public class MyServiceStub implements MyService { // ... } public class MyLauncherTest private MyLauncher myLauncher; private MyServiceStub myServiceStub; @Before public void setUp() { myServiceStub = new MyServiceStub(); myLauncher = new MyLauncher(myServiceStub); } @Test public void someTest() { } }

Esta técnica es especialmente útil si la prueba y la clase bajo prueba se ubican en el mismo paquete porque entonces puede usar el modificador de acceso predeterminado, package-private para evitar que otras clases accedan a él. Tenga en cuenta que aún puede tener su código de producción en src/main/java pero sus pruebas en src/main/test directorios src/main/test .

Si te gusta Mockito, entonces apreciarás el MockitoJUnitRunner . Te permite hacer cosas "mágicas" como @Manuel te mostró:

@RunWith(MockitoJUnitRunner.class) public class MyLauncherTest @InjectMocks private MyLauncher myLauncher; // no need to call the constructor @Mock private MyService myService; @Test public void someTest() { } }

Alternativamente, puede usar el corredor JUnit predeterminado y llamar a MockitoAnnotations.initMocks() en un método setUp() para permitir que Mockito inicialice los valores anotados. Puede encontrar más información en el javadoc de @InitMocks y en una publicación de blog que he escrito.

Tengo una configuración de componentes que es esencialmente un iniciador para una aplicación. Está configurado así:

@Component public class MyLauncher { @Autowired MyService myService; //other methods }

MyService está anotado con la anotación @Service Spring y está conectado automáticamente a mi clase de iniciador sin ningún problema.

Me gustaría escribir algunos casos de prueba jUnit para MyLauncher, para hacerlo comencé una clase como esta:

public class MyLauncherTest private MyLauncher myLauncher = new MyLauncher(); @Test public void someTest() { } }

¿Puedo crear un objeto Mock para MyService e insertarlo en myLauncher en mi clase de prueba? Actualmente no tengo un getter o setter en myLauncher ya que Spring está manejando el autoenvío. Si es posible, me gustaría no tener que agregar getters y setters. ¿Puedo decirle al caso de prueba que inyecte un objeto simulado en la variable autocabilitada usando un método @Before init?

Si me estoy equivocando, no dudes en decirlo. Todavía soy nuevo en esto. Mi objetivo principal es tener algún código Java o anotación que @Autowired un objeto simulado en esa variable @Autowired sin que tenga que escribir un método setter o tener que usar un archivo applicationContext-test.xml. Prefiero mantener todo para los casos de prueba en el archivo .java en lugar de tener que mantener un ApplicationContent por separado solo para mis pruebas.

Estoy esperando usar Mockito para los objetos simulados. En el pasado, he hecho esto usando org.mockito.Mockito y creando mis objetos con Mockito.mock(MyClass.class)

Muchas gracias.


Creo que para poder tener el trabajo de auto-cableado en su clase MyLauncher (para myService), tendrá que dejar que Spring lo inicialice en lugar de llamar al constructor, al autoconectar myLauncher. Una vez que se está conectando automáticamente (y myService también se está autoconectando), Spring (1.4.0 y superior) proporciona una anotación @MockBean que puede poner en su prueba. Esto reemplazará a los beans individuales coincidentes en contexto con un simulacro de ese tipo. Luego puede definir qué burla quiere, en un método @Before.

public class MyLauncherTest @MockBean private MyService myService; @Autowired private MyLauncher myLauncher; @Before private void setupMockBean() { doNothing().when(myService).someVoidMethod(); doReturn("Some Value").when(myService).someStringMethod(); } @Test public void someTest() { myLauncher.doSomething(); } }

Su clase MyLauncher puede permanecer sin modificaciones, y su bean MyService será un simulacro cuyos métodos devuelven valores como usted definió:

@Component public class MyLauncher { @Autowired MyService myService; public void doSomething() { myService.someVoidMethod(); myService.someMethodThatCallsSomeStringMethod(); } //other methods }

Un par de ventajas de esto sobre otros métodos mencionados es que:

  1. No necesita insertar manualmente myService.
  2. No necesita usar el corredor Mockito o las reglas.

La respuesta aceptada (use MockitoJUnitRunner y @InjectMocks ) es genial. Pero si quieres algo un poco más liviano (sin un corredor JUnit especial), y menos "mágico" (más transparente) especialmente para uso ocasional, puedes simplemente establecer los campos privados directamente usando introspección.

Si usa Spring, ya tiene una clase de utilidad para esto: org.springframework.test.util.ReflectionTestUtils

El uso es bastante sencillo:

ReflectionTestUtils.setField(myLauncher, "myService", myService);

El primer argumento es su bean objetivo, el segundo es el nombre del campo (generalmente privado) y el último es el valor para inyectar.

Si no usas Spring, es bastante trivial implementar dicho método de utilidad. Aquí está el código que utilicé antes de encontrar esta clase de primavera:

public static void setPrivateField(Object target, String fieldName, Object value){ try{ Field privateField = target.getClass().getDeclaredField(fieldName); privateField.setAccessible(true); privateField.set(target, value); }catch(Exception e){ throw new RuntimeException(e); } }


Mira este link

Luego escribe tu caso de prueba como

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"/applicationContext.xml"}) public class MyLauncherTest{ @Resource private MyLauncher myLauncher ; @Test public void someTest() { //test code } }


Puede inyectar absolutamente simulaciones en MyLauncher en su prueba. Estoy seguro de que si muestras el marco de burla que estás usando, serías rápido para dar una respuesta. Con mockito buscaría usar @RunWith (MockitoJUnitRunner.class) y usar anotaciones para myLauncher. Se vería algo así como lo que está abajo.

@RunWith(MockitoJUnitRunner.class) public class MyLauncherTest @InjectMocks private MyLauncher myLauncher = new MyLauncher(); @Mock private MyService myService; @Test public void someTest() { } }


Soy un nuevo usuario para Spring. Encontré una solución diferente para esto. Usar la reflexión y hacer públicos los campos necesarios y asignar objetos falsos.

Este es mi controlador de autenticación y tiene algunas propiedades privadas de Autowired.

@RestController public class AuthController { @Autowired private UsersDAOInterface usersDao; @Autowired private TokensDAOInterface tokensDao; @RequestMapping(path = "/auth/getToken", method = RequestMethod.POST) public @ResponseBody Object getToken(@RequestParam String username, @RequestParam String password) { User user = usersDao.getLoginUser(username, password); if (user == null) return new ErrorResult("Kullanıcıadı veya şifre hatalı"); Token token = new Token(); token.setTokenId("aergaerg"); token.setUserId(1); token.setInsertDatetime(new Date()); return token; } }

Y esta es mi prueba de Junit para AuthController. Estoy haciendo públicas las propiedades privadas necesarias y les asigno objetos falsos y rock :)

public class AuthControllerTest { @Test public void getToken() { try { UsersDAO mockUsersDao = mock(UsersDAO.class); TokensDAO mockTokensDao = mock(TokensDAO.class); User dummyUser = new User(); dummyUser.setId(10); dummyUser.setUsername("nixarsoft"); dummyUser.setTopId(0); when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) // .thenReturn(dummyUser); AuthController ctrl = new AuthController(); Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao"); usersDaoField.setAccessible(true); usersDaoField.set(ctrl, mockUsersDao); Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao"); tokensDaoField.setAccessible(true); tokensDaoField.set(ctrl, mockTokensDao); Token t = (Token) ctrl.getToken("test", "aergaeg"); Assert.assertNotNull(t); } catch (Exception ex) { System.out.println(ex); } } }

No conozco las ventajas y desventajas de esta manera, pero esto está funcionando. Esta técnica tiene un poco más de código, pero estos códigos se pueden separar por diferentes métodos, etc. Hay más buenas respuestas para esta pregunta, pero quiero apuntar a una solución diferente. Perdón por mi mal ingles. Que tengas un buen java para todos :)