java - test - tutorial pruebas unitarias android studio
Datos de primavera: pruebas de unidad de capa de servicio (6)
La pregunta puede ser un poco vieja, pero pondré una respuesta en caso de que alguien tropiece.
- Estoy usando Mockito y JUnit.
- AccountRepository es un repositorio de datos simple de primavera que extiende JPARepository.
- La cuenta es una entidad JPA simple.
Para probar sus servicios y simular repositorios de Spring Data, necesita algo como a continuación.
package foo.bar.service.impl;
import foo.bar.data.entity.Account;
import foo.bar.data.repository.AccountRepository;
import foo.bar.service.AccountService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class AccountServiceImplTest {
@Mock
private static AccountRepository accountRepository;
@InjectMocks
private static AccountService accountService = new AccountServiceImpl();
private Account account;
@Test
public void testFindAccount() {
Integer accountId = new Integer(1);
account = new Account();
account.setId(accountId);
account.setName("Account name");
account.setCode("Accont code");
account.setDescription("Account description");
Mockito.when(accountRepository.findOne(accountId)).thenReturn(account);
Account retrivedAccount = accountService.findAccount(accountId);
Assert.assertEquals(account, retrivedAccount);
}
}
En mi proyecto tengo problemas para hacer pruebas de unidad. Un problema es que solo hacer una prueba de integración es mucho más rápido de escribir y también prueba que los componentes realmente funcionan juntos. La prueba de unidad de "algoritmos" o algo así parece mucho más fácil Unidades de servicio de pruebas de unidad simplemente se siente mal e inútil.
Estoy usando mockito para simular el repositorio de datos spring (y, por lo tanto, acceso a DB). El problema es si le digo al repositorio simulado que devuelva la entidad A en el método de llamada getById, obviamente devolverá eso y el servicio también lo devolverá. Sí, el servicio hace algunas cosas adicionales, pero cosas muy pequeñas, como cargar colecciones perezosas (desde hibernación). Obviamente no tengo ninguna colección perezosa (proxies) en una prueba de unidad.
Ejemplo:
@Test
public void testGetById() {
System.out.println("getById");
TestCompound expResult = new TestCompound(id, "Test Compound", "9999-99-9", null, null, null);
TestCompoundRepository mockedRepository = mock(TestCompoundRepository.class);
when(mockedRepository.findOne(id)).thenReturn(expResult);
ReflectionTestUtils.setField(testCompoundService, "testCompoundRepository",
mockedRepository, TestCompoundRepository.class);
TestCompound result = testCompoundService.getById(id);
assertEquals(expResult, result);
}
Hurra, el resto lo consigue. ¡Qué sorpresa! No, realmente no.
¿Puede alguien explicarme lo que estoy haciendo mal? O bien, ¿cuál es el punto de tal prueba? Quiero decir que le digo que devuelva expResult y luego se devuelve. Guau. ¡Qué sorpresa! Se siente como si estuviera probando si mockito funciona y no mi servicio.
EDITAR:
El único beneficio que veo si algún error estúpido ocurre es como dejar una línea no deseada que establece el valor de retorno en nulo o algo similar. Tales casos serían atrapados por la prueba unitaria. ¿Todavía la relación "recompensa-esfuerzo" parece mala?
Puede utilizar esta biblioteca: https://github.com/agileapes/spring-data-mock
Esto simulará su repositorio para usted, mientras le permite implementar una funcionalidad personalizada para cualquier método, así como sus métodos de consulta nativos.
Puedes burlarte del repositorio e inyectarlo al servicio, esta es la forma; pero, si simplemente @Mock
una instancia del servicio con @Mock
de repositorios, sería mejor, si define los repositorios como campos private final
en el servicio y utiliza un constructor de todos los repositorios. De esa manera, si agrega otro repositorio al servicio, la prueba fallará y usted tendrá que cambiarlo, que es el propósito.
Imagina este servicio:
class OrderService {
private final UserRepository userRepos;
public OrderService(UserRepository userRepos) {
this.userRepos = userRepos;
}
...
}
Y esta prueba:
class OrderServiceTests {
@Mock
private UserRepository userRepos;
private OrderService service;
private OrderServiceTests() {
this.service = new OrderService(this.userRepos);
}
}
Ahora, si agregamos otra dependencia al servicio:
class OrderService {
private final UserRepository userRepos;
private final AddressRepository addRepos;
public OrderService(UserRepository userRepos, AddressRepository addRepos) {
this.userRepos = userRepos;
this.addRepos = addRepos;
...
}
La prueba anterior fallará porque el constructor ha cambiado. Si usa @InjectMocks
esto no sucederá; La inyección ocurre detrás de la cortina y no tenemos claro qué sucede; Esto puede no ser deseable.
Otra cosa es que no estoy de acuerdo con que la prueba de integración cubra todos los casos que cubrirán las pruebas unitarias; Puede pero no siempre es el caso. Incluso el controlador puede ser probado en unidades con simulacros; después de que todas las pruebas están destinadas a cubrir todo el código que hemos escrito, por lo que deben ser de grano fino; Imagínese cuando seguimos TTD y solo completamos el nivel de controlador y servicios: ¿cómo procedemos sin probar la unidad del controlador?
Suponiendo que tenemos la siguiente clase de servicio
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public Employee getEmployeeByName(String name) {
return employeeRepository.findByName(name);
}
}
Clase de prueba
@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {
@TestConfiguration
static class EmployeeServiceImplTestContextConfiguration {
@Bean
public EmployeeService employeeService() {
return new EmployeeServiceImpl();
}
}
@Autowired
private EmployeeService employeeService;
@MockBean
private EmployeeRepository employeeRepository;
// write test cases here
}
Para verificar la clase de servicio, necesitamos tener una instancia de la clase de servicio creada y disponible como @Bean para que podamos @Autowire en nuestra clase de prueba. Esta configuración se logra mediante el uso de la anotación @TestConfiguration .
Durante el escaneo de componentes, es posible que encontremos componentes o configuraciones creadas solo para pruebas específicas que se pueden recoger accidentalmente en todas partes. Para ayudar a evitar eso, Spring Boot proporciona la anotación @TestConfiguration que se puede usar en las clases en src / test / java para indicar que no deben ser detectadas por escaneo.
Otra cosa interesante aquí es el uso de @MockBean . Crea un simulacro para el EmployeeRepository que se puede utilizar para omitir la llamada al EmployeeRepository real:
@Before
public void setUp() {
Employee alex = new Employee("alex");
Mockito.when(employeeRepository.findByName(alex.getName()))
.thenReturn(alex);
}
Después de la configuración, podemos probar fácilmente nuestro servicio como:
@Test
public void whenValidName_thenEmployeeShouldBeFound() {
String name = "alex";
Employee found = employeeService.getEmployeeByName(name);
assertThat(found.getName())isEqualTo(name);
}
Para obtener información más detallada, visite https://www.baeldung.com/spring-boot-testing
Tienes toda la razón. Es clara la prueba unitaria. Y nunca fallará (entonces, es inútil) Creo que necesita una prueba de integración para probar el repositorio de JPA real con una base de datos real ( H2
en la memoria, por ejemplo) (como siempre lo hago).
Y es mejor probar tus servicios (sus interfaces). Si después de un tiempo cambiará su almacenamiento (a Mongo, por ejemplo), podrá utilizar las pruebas de servicio para asegurarse de que todo funcione como antes.
Después de un tiempo, se sorprenderá de cuántos problemas relacionados con DB / JPA (restricciones, bloqueos optimistas, carga diferida, identificaciones duplicadas, algunos problemas de hibernación, etc.) encontrará.
Además, intente desarrollarlo mediante pruebas, no solo escriba la prueba después de la implementación. En su lugar, antes de crear un nuevo método en servicio: cree una prueba para él, implemente el método de servicio y solo después de volver a verificarlo en la aplicación real. Al menos es mucho más rápido iniciar una prueba que un servidor.
Por lo tanto, no cree pruebas para tener un montón de ellos. Encuentra como pueden ayudarte.
El uso de simulacros para repositorios no es una buena idea. Pruebe cómo funcionan sus servicios junto con Hibernate / JPA / Database. La mayor parte de los problemas se encuentra entre capas .
Una de las razones por las que me gusta probar mis repositorios de Spring Data es para comprobar que he definido correctamente mis asignaciones JPA. No utilizo un marco de simulacro para estas pruebas, uso el marco de Spring Test, que en realidad reinicia el contenedor, lo que me permite conectar automáticamente el repositorio real a la prueba de Junit para que pueda ejecutar pruebas en su contra.
Estoy de acuerdo con sus pensamientos de que burlarse del repositorio es bastante inútil. Desde que utiliza Spring, sugeriría aprovechar el marco de prueba de Spring para realizar pruebas reales en sus repositorios, que se pueden ejecutar en una base de datos integrada como H2 de una manera más basada en pruebas unitarias o en la implementación de su base de datos real, como Oracle o MySql, para Realizar más de una prueba de integración. (Ejecute esto contra una copia de una base de datos de desarrollo) Estas pruebas revelarán falacias en sus asignaciones JPA y otros elementos, como la configuración incorrecta de cascadas en la base de datos.
Aquí hay un example de una de mis pruebas en GitHub. Observe cómo el marco realmente habilita al repositorio en la prueba. El repositorio también contiene un ejemplo de cómo configurar el marco de Spring Test, que también he demostrado en esta publicación del blog .
En conclusión, no creo que reciba ninguna de las ventajas de probar un repositorio que he discutido sobre el uso de un simulacro del repositorio.
Una nota adicional que quería agregar, es que los simulacros no están realmente destinados a usarse en la clase real bajo prueba. Su uso es para proporcionar dependencias requeridas a una clase en prueba.