Quiero un repositorio (digamos UserRepository
) creado con la ayuda de Spring Data. Soy nuevo en spring-data (pero no en spring) y uso este tutorial . Mi elección de tecnologías para tratar con la base de datos es JPA 2.1 e Hibernate. El problema es que no tengo idea de cómo escribir pruebas unitarias para dicho repositorio.
Tomemos el create()
método por ejemplo. Como estoy trabajando primero en la prueba, se supone que debo escribir una prueba unitaria para ello, y ahí es donde me encuentro con tres problemas:
Primero, ¿cómo puedo inyectar un simulacro
EntityManager
en la implementación no existente de unaUserRepository
interfaz? Spring Data generaría una implementación basada en esta interfaz:public interface UserRepository extends CrudRepository<User, Long> {}
Sin embargo, no sé cómo obligarlo a usar un
EntityManager
simulacro y otros simulacros; si hubiera escrito la implementación yo mismo, probablemente tendría un método de establecimiento paraEntityManager
permitirme usar mi simulacro para la prueba de la unidad. (En cuanto a la conectividad de base de datos real, tengo unaJpaConfiguration
clase, con la anotación@Configuration
y@EnableJpaRepositories
, que define programación frijoles paraDataSource
,EntityManagerFactory
,EntityManager
etc - pero repositorios deberán ser comprobadas amable y permitir la redefinición de estas cosas).En segundo lugar, ¿debo probar las interacciones? Es difícil para mí descubrir qué métodos
EntityManager
y quéQuery
se supone que se deben llamar (similar a esoverify(entityManager).createNamedQuery(anyString()).getResultList();
), ya que no soy yo quien está escribiendo la implementación.En tercer lugar, ¿se supone que debo probar los métodos generados por Spring-Data en primer lugar? Como sé, no se supone que el código de la biblioteca de terceros sea probado por la unidad, solo el código que los desarrolladores escriben ellos mismos debe ser probado por la unidad. Pero si eso es cierto, todavía trae la primera pregunta a la escena: digamos, tengo un par de métodos personalizados para mi repositorio, para lo cual escribiré la implementación, ¿cómo puedo inyectar mis simulacros
EntityManager
yQuery
generarlos? ¿repositorio?
Nota: Probaré mis repositorios utilizando tanto la integración como las pruebas unitarias. Para mis pruebas de integración, estoy usando una base de datos en memoria HSQL, y obviamente no estoy usando una base de datos para pruebas unitarias.
Y probablemente la cuarta pregunta, ¿es correcto probar la creación correcta del gráfico de objeto y la recuperación del gráfico de objeto en las pruebas de integración (por ejemplo, tengo un gráfico de objeto complejo definido con Hibernate)?
Actualización: hoy he seguido experimentando con la inyección simulada: he creado una clase interna estática para permitir la inyección simulada.
@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, ejecutar 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
fuente
pom.xml
.Con Spring Boot + Spring Data se ha vuelto bastante fácil:
La solución de @heez muestra el contexto completo, esto solo muestra lo que se necesita para que JPA + Transaction funcione. Tenga en cuenta que la solución anterior mostrará una base de datos de prueba en memoria dado que se puede encontrar una en el classpath.
fuente
@RunWith(SpringRuner.class)
ahora ya está incluido en el@DataJpaTest
.@RunWith(SpringRunner.class
inicia el contexto de primavera, lo que significa que está comprobando la integración entre varias unidades. Prueba de unidad es probar una sola unidad -> clase única. Luego escribeMyClass sut = new MyClass();
y prueba el objeto sut (sut = servicio bajo prueba)Esto puede llegar un poco tarde, pero he escrito algo para este mismo propósito. Mi biblioteca se burlará de los métodos básicos de repositorio crud para usted e interpretará la mayoría de las funcionalidades de sus métodos de consulta. Tendrá que inyectar funcionalidades para sus propias consultas nativas, pero el resto lo hará por usted.
Echar un vistazo:
https://github.com/mmnaseri/spring-data-mock
ACTUALIZAR
Esto está ahora en Maven central y en muy buena forma.
fuente
Si está usando Spring Boot, simplemente puede usar
@SpringBootTest
para cargar en suApplicationContext
(que es lo que le está ladrando su stacktrace). Esto le permite conectar automáticamente en sus repositorios de datos de primavera. Asegúrese de agregar@RunWith(SpringRunner.class)
para que se recojan las anotaciones específicas del resorte:Puede leer más sobre las pruebas en el arranque de primavera en sus documentos .
fuente
Predicate
s (que era mi caso de uso) funciona bastante bien.En la última versión de spring boot 2.1.1.RELEASE , es simple como:
Código completo:
https://github.com/jrichardsz/spring-boot-templates/blob/master/003-hql-database-with-integration-test/src/test/java/test/CustomerRepositoryIntegrationTest.java
fuente
2.0.0.RELEASE
Spring Boot.Cuando realmente desee escribir una prueba i para un repositorio de datos de Spring, puede hacerlo así:
Para seguir este ejemplo, debe usar estas dependencias:
fuente
Resolví esto usando de esta manera:
fuente
Con JUnit5 y la
@DataJpaTest
prueba se verá así (código kotlin):Se podría utilizar
TestEntityManager
deorg.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
paquete con el fin de validar estado de entidad.fuente