Cómo probar la Primavera de repositorios de Datos?

Quiero un repositorio (por ejemplo, UserRepository), creado con la ayuda de la Primavera de Datos. Soy nuevo en la primavera de datos (pero no a la primavera) y yo uso este tutorial. Mi elección de tecnologías para tratar con la base de datos es JPA 2.1 y de Hibernación. El problema es que no puedo ni idea de cómo escribir pruebas unitarias para un repositorio.

Vamos a tomar create() método, por ejemplo. Como estoy trabajando de pruebas en primer lugar, se supone que debo escribir una prueba de unidad para ella – y que es donde me encuentro en tres problemas:

  • Primero, ¿cómo puedo inyectar un simulacro de un EntityManager en el que no existe la aplicación de un UserRepository interfaz? La primavera de Datos, se genere una aplicación basada en esta interfaz:

    public interface UserRepository extends CrudRepository<User, Long> {}

    Sin embargo, no sé cómo forzar el uso de un EntityManager simulacros y otras burla – si yo hubiera escrito la implementación de mí mismo, yo probablemente habría un método setter para EntityManager, lo que me permite usar mi simulacro de la prueba de la unidad. (Como real, conectividad de base de datos, tengo un JpaConfiguration clase, anotado con @Configuration y @EnableJpaRepositories, que mediante programación define frijoles para DataSource, EntityManagerFactory, EntityManager etc. – pero repositorios debe ser la prueba de usar y permiten pasar por encima de estas cosas).

  • Segundo, debo probar para las interacciones? Es difícil para mí para averiguar qué métodos de EntityManager y Query se supone que se llama (similar a la que verify(entityManager).createNamedQuery(anyString()).getResultList();), ya no soy yo quien está escribiendo la aplicación.

  • Tercero, se supone que voy a la unidad de pruebas de la Primavera de Datos generados por los métodos en primer lugar? Que yo sepa, la tercera parte del código de la biblioteca no se supone que la unidad probada – sólo el código, los desarrolladores escribir ellos mismos se supone que la unidad probada. Pero si eso es cierto, todavía trae la primera pregunta de nuevo a la escena: por ejemplo, tengo un par de métodos personalizados para mi repositorio, para que voy a estar escribiendo la aplicación, ¿cómo puedo inyectar mi se burla de EntityManager y Query en la final, generado repositorio?

Nota: voy a ser la prueba de conducción mis repositorios utilizando tanto la integración y la unidad de pruebas. Para mi las pruebas de integración estoy utilizando un HSQL base de datos en memoria, y yo, obviamente, no utiliza una base de datos para pruebas de unidad.

Y probablemente la cuarta pregunta, es correcto para probar el correcto gráfico de objetos creación de objetos y gráfico de recuperación en las pruebas de integración (es decir, tengo un gráfico de objetos complejos definidos con Hibernate)?

Actualización: hoy he seguido experimentando con simulacros de inyección – he creado una clase interna estática para permitir el simulacro de la inyección.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {

@Configuration
@EnableJpaRepositories(basePackages = "com.anything.repository")
static class TestConfiguration {

    @Bean
    public EntityManagerFactory entityManagerFactory() {
        return mock(EntityManagerFactory.class);
    }

    @Bean
    public EntityManager entityManager() {
        EntityManager entityManagerMock = mock(EntityManager.class);
        //when(entityManagerMock.getMetamodel()).thenReturn(mock(Metamodel.class));
        when(entityManagerMock.getMetamodel()).thenReturn(mock(MetamodelImpl.class));
        return entityManagerMock;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return mock(JpaTransactionManager.class);
    }

}

@Autowired
private UserRepository userRepository;

@Autowired
private EntityManager entityManager;

@Test
public void shouldSaveUser() {
    User user = new UserBuilder().build();
    userRepository.save(user);
    verify(entityManager.createNamedQuery(anyString()).executeUpdate());
}

}

Sin embargo, la ejecución de esta prueba me da el siguiente stacktrace:

java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1493)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:684)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)
    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
    ... 28 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1489)
    ... 44 more
InformationsquelleAutor user1797032 | 2014-05-02

8 Kommentare

  1. 100

    tl;dr

    Para hacerla corta, no hay forma de unidad de prueba de Spring Data JPA repositorios razonable por una razón muy simple: es la forma a engorroso para burlarse de todas las partes de la API de JPA invocamos al arranque de los repositorios. La unidad de pruebas no tiene demasiado sentido aquí de todos modos, como por lo general, no escribir ningún código de implementación del mismo (ver el siguiente párrafo en implementaciones personalizadas), de modo que las pruebas de integración es el más razonable.

    Detalles

    Podemos hacer mucho por adelantado de la validación y de la instalación para asegurarse de que sólo se puede bootstrap una aplicación que no válidos derivados de consultas, etc.

    • Creamos y caché CriteriaQuery instancias de derivados de consultas para asegurarse de que los métodos de consulta no contienen ningún error. Esto requiere trabajar con la API de Criterios así como la meta.modelo.
    • Verificamos manualmente consultas definidas por pedir a la EntityManager para crear un Query ejemplo para aquellos (que activa eficazmente la sintaxis de la consulta de validación).
    • Inspeccionar los Metamodel para el meta-datos acerca de los tipos de dominio se maneja a preparar es nuevo cheques etc.

    Todas las cosas que usted probablemente iba a aplazar en un escrito a mano repositorio que podría causar que la aplicación no funcione en tiempo de ejecución (debido a la no válido consultas, etc.).

    Si usted piensa acerca de ello, no hay ningún código que se escribe para sus repositorios, por lo que no hay necesidad de escribir ningún unidadpruebas. Simplemente no hay necesidad de como usted puede confiar en nuestro test base a la captura básica de bugs (si todavía pasar a ejecutar en uno, siéntase libre de criar a un billete). Sin embargo, definitivamente hay necesidad de pruebas de integración para la prueba de dos aspectos de su capa de persistencia como son los aspectos relacionados con su dominio:

    • asignaciones de entidad
    • la semántica de consulta (sintaxis es verificado en cada bootstrap intento de todos modos).

    Pruebas de integración

    Esto se hace generalmente mediante el uso de una base de datos en memoria y casos de prueba de bootstrap un Resorte ApplicationContext generalmente a través de la prueba contexto del marco (como ya lo hace), pre-poblar la base de datos (mediante la inserción de instancias de objetos a través de la EntityManager o repo, o a través de un simple archivo de SQL) y, a continuación, ejecute la consulta de los métodos para comprobar el resultado de ellos.

    Pruebas de implementaciones personalizadas

    Implementación personalizada partes del repositorio son escrito de una manera que ellos no tienen que saber acerca de Spring Data JPA. Son simples Primavera frijoles que un gen EntityManager inyectado. Usted podría, por supuesto que quiero tratar de burlarse de las interacciones con él, pero para ser honesto, la unidad de pruebas de la JPA no ha sido una muy grata experiencia para nosotros, ya que funciona con un buen montón de indirections (EntityManager -> CriteriaBuilder, CriteriaQuery etc.) para terminar con burla se burla de regresar y así sucesivamente.

    • ¿Tienes un enlace a un pequeño ejemplo de un test de integración con una base de datos en memoria (por ejemplo, h2) ?
    • Los ejemplos aquí el uso de HSQLDB. El cambio a H2 es básicamente una cuestión de intercambio de la dependencia en el pom.xml.
    • Gracias, pero yo estaba esperando a ver un ejemplo que pre-rellena la base de datos y/o comprueba la base de datos.
    • El enlace detrás de «escrito en una forma» no funciona más. Tal vez usted puede actualizar?
    • Así, se propone el uso de pruebas de integración en lugar de pruebas de unidad para implementaciones personalizadas demasiado? Y no escribir pruebas unitarias para todo? Solo para aclarar. Está bien si sí. Entiendo la razón (demasiado complejo para burlarse de todas las cosas). Soy nuevo en el JPA pruebas, de manera que sólo quiero averiguar.
    • Hablando en general: sí.

  2. 37

    Con Spring Boot + Primavera de Datos se ha convertido en muy fácil:

    @RunWith(SpringRunner.class)
    @DataJpaTest
    public class MyRepositoryTest {
    
        @Autowired
        MyRepository subject;
    
        @Test
        public void myTest() throws Exception {
            subject.save(new MyEntity());
        }
    }

    La solución por @heez trae el contexto completo, esto sólo traer a colación lo que se necesita para JPA+de Transacción para el trabajo.
    Tenga en cuenta que la solución anterior, se abrirá un en la prueba de memoria de la base de datos dado que uno puede encontrar en la ruta de clases.

    • Esto es un integración de la prueba, no unidad prueba de que OP mencionado
    • Tienes razón acerca de la terminología. Sin embargo, Dado que la Primavera de Datos implementa la interfaz para usted, usted es difícil de usar de la Primavera, y en ese momento se convierten en un test de integración. Si yo le preguntara a una pregunta como esta probablemente yo también pidió una prueba de unidad sin pensar acerca de la terminología. Por lo tanto yo no veo como el principal, o incluso central, el punto de la cuestión.
  3. 20

    Esto puede ser un poco demasiado tarde, pero tengo que escribir algo para este fin. Mi biblioteca se burlan de las crud repositorio de métodos para usted, así como de interpretar la mayoría de las funcionalidades de sus métodos de consulta.
    Usted tendrá que inyectar funcionalidades para su propio consultas, pero el resto está hecho para usted.

    A echar un vistazo:

    https://github.com/mmnaseri/spring-data-mock

    ACTUALIZACIÓN

    Esto es ahora en Maven central y en muy buena forma.

  4. 14

    Si usted está utilizando la Primavera de Inicio, usted puede simplemente utilizar @SpringBootTest a la carga en su ApplicationContext (que es lo que tu stacktrace está ladrando a usted acerca de). Esto le permite autowire en su primavera-repositorios de datos. Asegúrese de agregar @RunWith(SpringRunner.class) para la primavera-anotaciones específicas son recogidos:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class OrphanManagementTest {
    
      @Autowired
      private UserRepository userRepository;
    
      @Test
      public void saveTest() {
        User user = new User("Tom");
        userRepository.save(user);
        Assert.assertNotNull(userRepository.findOne("Tom"));
      }
    }

    Usted puede leer más acerca de las pruebas de la primavera de arranque en su docs.

    • Este es un muy buen ejemplo, pero simplista, en mi opinión. Hay situaciones en las que esta prueba puede incluso no ??
    • No es este uno de por sí, pero supongamos que usted quería probar Predicates (que era mi caso de uso) funciona bastante bien.
    • para mí repositorio siempre es null. Alguna ayuda?
    • Esta es mi humilde opinión la mejor respuesta. Este modo de probar la CrudRepo, la Entidad y el DDL de secuencias de comandos que crea la Entidad de la tabla(s).
    • He escrito un ensayo exactamente como esta. Funciona perfectamente cuando la aplicación del Repositorio utiliza jdbcTemplate. Sin embargo, cuando puedo cambiar la aplicación para la primavera-datos (mediante la extensión de la interfaz del Repositorio), la prueba falla y userRepository.findOne devuelve null. Alguna idea de cómo solucionar esto?
  5. 5

    Cuando usted realmente quiere escribir una prueba para una primavera repositorio de datos que usted puede hacer esto de esta manera:

    @RunWith(SpringRunner.class)
    @DataJpaTest
    @EnableJpaRepositories(basePackageClasses = WebBookingRepository.class)
    @EntityScan(basePackageClasses = WebBooking.class)
    public class WebBookingRepositoryIntegrationTest {
    
        @Autowired
        private WebBookingRepository repository;
    
        @Test
        public void testSaveAndFindAll() {
            WebBooking webBooking = new WebBooking();
            webBooking.setUuid("some uuid");
            webBooking.setItems(Arrays.asList(new WebBookingItem()));
            repository.save(webBooking);
    
            Iterable<WebBooking> findAll = repository.findAll();
    
            assertThat(findAll).hasSize(1);
            webBooking.setId(1L);
            assertThat(findAll).containsOnly(webBooking);
        }
    }

    A seguir este ejemplo tenemos el uso de estas dependencias:

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.197</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.9.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
  6. 4

    Lo resuelto por el uso de esta manera –

        @RunWith(SpringRunner.class)
        @EnableJpaRepositories(basePackages={"com.path.repositories"})
        @EntityScan(basePackages={"com.model"})
        @TestPropertySource("classpath:application.properties")
        @ContextConfiguration(classes = {ApiTestConfig.class,SaveActionsServiceImpl.class})
        public class SaveCriticalProcedureTest {
    
            @Autowired
            private SaveActionsService saveActionsService;
            .......
            .......
    }
  7. 4

    En la última versión de la primavera de arranque 2.1.1.LIBERACIÓN, es simple como :

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = SampleApplication.class)
    public class CustomerRepositoryIntegrationTest {
    
        @Autowired
        CustomerRepository repository;
    
        @Test
        public void myTest() throws Exception {
    
            Customer customer = new Customer();
            customer.setId(100l);
            customer.setFirstName("John");
            customer.setLastName("Wick");
    
            repository.save(customer);
    
            List<?> queryResult = repository.findByLastName("Wick");
    
            assertFalse(queryResult.isEmpty());
            assertNotNull(queryResult.get(0));
        }
    }

    Código completo:

    https://github.com/jrichardsz/spring-boot-templates/blob/master/003-hql-database-with-integration-test/src/test/java/test/CustomerRepositoryIntegrationTest.java

    • Esto es bastante incompleta ‘ejemplo’: no puede ser construida, la «integración» de las pruebas utiliza la misma configuración que el código de producción. Es decir. bueno para nada.
    • Me disculpo. Voy a azotar a mí, porque de este error. Por favor, inténtelo de nuevo!
    • Esto también funciona con 2.0.0.RELEASE de la Primavera de Arranque.
  8. 2

    Con JUnit5 y @DataJpaTest prueba de apariencia (kotlin código):

    @DataJpaTest
    @ExtendWith(value = [SpringExtension::class])
    class ActivityJpaTest {
    
        @Autowired
        lateinit var entityManager: TestEntityManager
    
        @Autowired
        lateinit var myEntityRepository: MyEntityRepository
    
        @Test
        fun shouldSaveEntity() {
            //when
            val savedEntity = myEntityRepository.save(MyEntity(1, "test")
    
            //then 
            Assertions.assertNotNull(entityManager.find(MyEntity::class.java, savedEntity.id))
        }
    }

    Usted podría utilizar TestEntityManager de org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager paquete con el fin de validar la entidad estatal.

    • Siempre es mejor resorte para generar el Id para el bean de entidad.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea