尝试使用 Spring 数据 JPA 存储库 returns NullPointerException 执行集成测试
Attempting to perform Integration Test with Spring Data JPA Repositories returns NullPointerException
我正在尝试为 Spring 4 MVC 项目编写集成测试。在我使用的项目中 Spring Data JPA Repositories 和 Hibernate。
下面是两个文件,集成测试本身和用于设置持久性配置的配置。
PersistenceTestConfiguration.java
package <project>.cms.rest.test.configuration;
import <project>.cms.rest.entity.EntityMarker;
import <project>.cms.rest.repository.RepositoryMarker;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.hibernate3.encryptor.HibernatePBEEncryptorRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackageClasses = {RepositoryMarker.class})
public class PersistenceTestConfiguration {
@Bean
public EntityManagerFactory entityManagerFactory() throws NamingException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan(EntityMarker.class.getPackage().getName());
factoryBean.setDataSource(dataSource());
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() throws NamingException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
@Bean
public HibernateExceptionTranslator exceptionTranslator() {
return new HibernateExceptionTranslator();
}
@Bean
public EmbeddedDatabase dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
}
我遇到的问题是,虽然我的 PersistenceTestConfiguration class 应该实例化适当的存储库 - 它们进入服务 classes 为 null。
如果我在测试中手动 @Mock 存储库 Class 那么它们在服务中不再为 null,但在例如调用 .save(entity) 方法时将始终 return null .
我怀疑我在做一些明显愚蠢但似乎无法弄清楚的事情。
任何正确方向的指示都将不胜感激。
编辑 1:
尽可能简单的测试
package <project>.cms.rest.test.service;
import <project>.cms.rest.resource.PostalAddressResource;
import <project>.cms.rest.service.PostalAddressService;
import <project>.cms.rest.service.impl.PostalAddressServiceImpl;
import <project>.cms.rest.test.configuration.PersistenceTestConfiguration;
import liquibase.Liquibase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.LiquibaseException;
import liquibase.resource.FileSystemResourceAccessor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import javax.inject.Inject;
import java.sql.Connection;
@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { PersistenceTestConfiguration.class })
@TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class PostalAddressServiceIntegrationTest {
@InjectMocks
private PostalAddressService postalAddressService = new PostalAddressServiceImpl();
@Inject
private EmbeddedDatabase database;
@Before
public void setupDatabase() throws java.sql.SQLException, LiquibaseException {
MockitoAnnotations.initMocks(this);
Liquibase liquibase = new Liquibase(
"src/liquibase/liquibase-master.xml",
new FileSystemResourceAccessor(),
new JdbcConnection(database.getConnection())
);
liquibase.update("");
}
@After
public void tearDown() throws java.sql.SQLException, LiquibaseException {
Connection connection = database.getConnection();
JdbcConnection liquibaseConnection = new JdbcConnection(connection);
Liquibase liquibase = new Liquibase(
"src/liquibase/liquibase-master.xml",
new FileSystemResourceAccessor(),
liquibaseConnection
);
liquibase.dropAll();
}
@Test
public void testAddNewPostalAddress() {
PostalAddressResource postalAddressResource = new PostalAddressResource();
postalAddressResource.setBuildingNameOrNumber("1");
postalAddressResource.setFirstLine("Test Lane");
postalAddressResource.setSecondLine("Testville");
postalAddressResource.setCountry("Testshire");
postalAddressResource.setPostCode("TE5 7ER");
postalAddressResource.setCountry("United Kingdom");
PostalAddressResource result = postalAddressService.createPostalAddress(postalAddressResource);
Assert.notNull(result);
}
}
生成的堆栈跟踪
java.lang.NullPointerException
at <project>.cms.rest.service.impl.PostalAddressServiceImpl.createPostalAddress(PostalAddressServiceImpl.java:66)
at <project>.cms.rest.test.service.PostalAddressServiceIntegrationTest.testAddNewPostalAddress(PostalAddressServiceIntegrationTest.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.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:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
PostalAddressServiceImpl的第66行如下:
} while(repository.countByPublicId(publicId) > 0);
编辑 2:
创建一个直接与存储库一起工作而不是通过服务工作的测试,具有:
@Inject
private PostalAddressRepository repository;
在顶部表现得很好。
所以似乎是在@InjectMocks 应该连接服务的依赖项时 class 出现了问题,导致存储库为空。
看看我得到了什么 运行 类似的东西,我也有一个这样定义的加载程序
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,
和一些测试听众
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
集成测试在 Spring 中有点乱,但在 Spring Boot 1.4 中已经整理了很多:
https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
已根据以下内容解决:
更改我的服务,让构造函数接受依赖项,而不是希望@InjectMocks 会完成它应该做的工作。
我正在尝试为 Spring 4 MVC 项目编写集成测试。在我使用的项目中 Spring Data JPA Repositories 和 Hibernate。
下面是两个文件,集成测试本身和用于设置持久性配置的配置。
PersistenceTestConfiguration.java
package <project>.cms.rest.test.configuration;
import <project>.cms.rest.entity.EntityMarker;
import <project>.cms.rest.repository.RepositoryMarker;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.hibernate3.encryptor.HibernatePBEEncryptorRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackageClasses = {RepositoryMarker.class})
public class PersistenceTestConfiguration {
@Bean
public EntityManagerFactory entityManagerFactory() throws NamingException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan(EntityMarker.class.getPackage().getName());
factoryBean.setDataSource(dataSource());
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() throws NamingException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
@Bean
public HibernateExceptionTranslator exceptionTranslator() {
return new HibernateExceptionTranslator();
}
@Bean
public EmbeddedDatabase dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
}
我遇到的问题是,虽然我的 PersistenceTestConfiguration class 应该实例化适当的存储库 - 它们进入服务 classes 为 null。
如果我在测试中手动 @Mock 存储库 Class 那么它们在服务中不再为 null,但在例如调用 .save(entity) 方法时将始终 return null .
我怀疑我在做一些明显愚蠢但似乎无法弄清楚的事情。
任何正确方向的指示都将不胜感激。
编辑 1:
尽可能简单的测试
package <project>.cms.rest.test.service;
import <project>.cms.rest.resource.PostalAddressResource;
import <project>.cms.rest.service.PostalAddressService;
import <project>.cms.rest.service.impl.PostalAddressServiceImpl;
import <project>.cms.rest.test.configuration.PersistenceTestConfiguration;
import liquibase.Liquibase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.LiquibaseException;
import liquibase.resource.FileSystemResourceAccessor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import javax.inject.Inject;
import java.sql.Connection;
@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { PersistenceTestConfiguration.class })
@TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class PostalAddressServiceIntegrationTest {
@InjectMocks
private PostalAddressService postalAddressService = new PostalAddressServiceImpl();
@Inject
private EmbeddedDatabase database;
@Before
public void setupDatabase() throws java.sql.SQLException, LiquibaseException {
MockitoAnnotations.initMocks(this);
Liquibase liquibase = new Liquibase(
"src/liquibase/liquibase-master.xml",
new FileSystemResourceAccessor(),
new JdbcConnection(database.getConnection())
);
liquibase.update("");
}
@After
public void tearDown() throws java.sql.SQLException, LiquibaseException {
Connection connection = database.getConnection();
JdbcConnection liquibaseConnection = new JdbcConnection(connection);
Liquibase liquibase = new Liquibase(
"src/liquibase/liquibase-master.xml",
new FileSystemResourceAccessor(),
liquibaseConnection
);
liquibase.dropAll();
}
@Test
public void testAddNewPostalAddress() {
PostalAddressResource postalAddressResource = new PostalAddressResource();
postalAddressResource.setBuildingNameOrNumber("1");
postalAddressResource.setFirstLine("Test Lane");
postalAddressResource.setSecondLine("Testville");
postalAddressResource.setCountry("Testshire");
postalAddressResource.setPostCode("TE5 7ER");
postalAddressResource.setCountry("United Kingdom");
PostalAddressResource result = postalAddressService.createPostalAddress(postalAddressResource);
Assert.notNull(result);
}
}
生成的堆栈跟踪
java.lang.NullPointerException
at <project>.cms.rest.service.impl.PostalAddressServiceImpl.createPostalAddress(PostalAddressServiceImpl.java:66)
at <project>.cms.rest.test.service.PostalAddressServiceIntegrationTest.testAddNewPostalAddress(PostalAddressServiceIntegrationTest.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.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:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
PostalAddressServiceImpl的第66行如下:
} while(repository.countByPublicId(publicId) > 0);
编辑 2:
创建一个直接与存储库一起工作而不是通过服务工作的测试,具有:
@Inject
private PostalAddressRepository repository;
在顶部表现得很好。
所以似乎是在@InjectMocks 应该连接服务的依赖项时 class 出现了问题,导致存储库为空。
看看我得到了什么 运行 类似的东西,我也有一个这样定义的加载程序
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,
和一些测试听众
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
集成测试在 Spring 中有点乱,但在 Spring Boot 1.4 中已经整理了很多:
https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
已根据以下内容解决:
更改我的服务,让构造函数接受依赖项,而不是希望@InjectMocks 会完成它应该做的工作。