在 CDI-Unit 中注入 @PersistenceContext
Injection of @PersistenceContext in CDI-Unit
这是单元测试代码。当我们运行 单元测试代码(SampleServiceTest2);在 AbstractDao 中注入的 EntityManager 总是空的!我们如何在单元测试期间注入 em。
*** SampleServiceTest2.java
import javax.inject.Inject;
import org.jglue.cdiunit.CdiRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(CdiRunner.class)
public class SampleServiceTest2 {
@Inject SampleService greeter;
@Test
public void testGreeter() throws Exception {
System.out.println("before2");
greeter.addSampleData(new SampleDataDto(), new KullaniciDto());
System.out.println("after2");
}
}
*** SampleService.java
import javax.ejb.Stateless;
import javax.inject.Inject;
....
@Stateless
@SecuredBean
public class SampleService {
@Inject
SampleLogic sampleLogic;
@Yetki(tag="perm_add_sample_data")
public void addSampleData(SampleDataDto data, KullaniciDto aktifKullaniciDto){
SampleDataHelper sampleDataHelper = new SampleDataHelper();
SampleData sampleData = sampleDataHelper.getEntity(data);
KullaniciHelper kullaniciHelper = new KullaniciHelper();
Kullanici kullanici = kullaniciHelper.getEntity(aktifKullaniciDto);
sampleLogic.addData(sampleData, kullanici);
}
}
**** SampleLogic.java
import javax.inject.Inject;
....
public class SampleLogic {
@Inject
SampleDataDao sampleDataDao;
public void addData(SampleData data, Kullanici kullanici) {
addData1(data,kullanici);
System.out.println("SampleLogic : addData() called!");
}
public void addData1(SampleData data, Kullanici kullanici) {
sampleDataDao.create(data, kullanici);
}
}
**** SampleDataDao.java
public class SampleDataDao extends AbstractDao<SampleData> {
private static final long serialVersionUID = 1L;
}
**** AbstractDao.java
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
private static final long serialVersionUID = 1L;
@PersistenceContext(unitName="meopdb")
private EntityManager em;
protected EntityManager getEm() {
return em;
}
@SuppressWarnings("rawtypes")
private Class entityClass;
@SuppressWarnings("rawtypes")
private Class getEntityClass() {
if (entityClass == null) {
entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
return entityClass;
}
public T create(T t, Kullanici kullanici) {
if (t.getId() != null) {
throw new IllegalStateException("Create Operation: Oid should be null");
}
t.setId(getSeqNextValue(t));
t.setEklemeZamani(new Timestamp(Calendar.getInstance().getTimeInMillis()));
t.setEkleyenKullaniciId(kullanici.getId());
t.setDurumId(EnumDurum.AKTIF.getValue());
t = em.merge(t);
em.flush();
return t;
}
}
如果您使用 CDIUnit 进行测试,您得到的唯一结果是 CDI 注入,而不是 Java EE 的全部功能。使用 @PersistenceContext
将 entityManager 注入 AbstractDAO
不是独立 CDI 的一部分,仅当应用程序在 Java EE 应用程序服务器中 运行ning 时才受支持。
解决方法是使用CDI机制注入EntityManager,创建生产者。然后可以将生产者切换为单元测试中的替代方案以提供测试 entityManager。但是,在独立单元测试中设置 JPA 并不是那么简单,因为您需要直接在 persistence.xml 文件中指定连接属性。另外,不要忘记将 JPA 实现(hibernate、eclipselink)的依赖项添加到测试依赖项中。
但是,如果您不想调整应用程序的代码,或者您在测试中需要的不仅仅是 CDI,您应该看看 Arquillian Java EE test framework。
这是 CDIUnit 的示例:
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
...
@Inject
@Named("meopdb")
private EntityManager em;
...
}
// producer in application - just a wraper over `@PersisteneContext`
public class EntityManagerProducer {
@Produces
@PersistenceContext(unitName="meopdb")
@Named("meopdb")
private EntityManager em;
}
/* producer in your test sources - it creates entityManager via API calls instead of injecting via `@PersistenceContext`. Also, a different persistence unit is used so that it does not clash with main persistence unit, which requires datasource from app server
*/
public TestEntityManagerProducer {
@Produces
@ProducesAlternative // CDIUnit annotation to turn this on as an alternative automatically
@Named("meopdb")
public EntityManager getEm() {
return Persistence
.createEntityManagerFactory("meopdb-test")
.createEntityManager();
}
}
这还不够。您需要使用名为 "meopdb-test" 的测试持久性单元在您的测试资源中创建一个新的 persistence.xml
。对于此单元,您需要指定 RESOURCE_LOCAL
transaction-type
,并指定连接信息。最后不要忘记 - 您需要在 persistence.xml 或外部 orm 文件中列出所有实体。这是因为您的测试 运行 在应用程序服务器之外。在应用服务器内部,JPA 可以自动找到实体。
正如@OndroMih 所说,在 CDI-Unit 中,您唯一得到的就是 CDI 注入。所以你要作弊一点。
您可以使用扩展为所有 @PersistenceContext
注入添加 javax.inject.Inject
注释
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.*;
import javax.inject.Inject;
import javax.persistence.PersistenceContext;
import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
public class AddInjectToPersistenceContextInjectionsCdiExtension implements Extension {
<T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
Set<AnnotatedField<? super T>> fields = pat.getAnnotatedType().getFields();
for (AnnotatedField<? super T> field : fields) {
if (shouldInjectionAnnotationBeAddedToField(field)) {
AnnotatedType<T> at = pat.getAnnotatedType();
AnnotatedTypeBuilder<T> builder = new AnnotatedTypeBuilder<T>().readFromType(at);
Inject injectAnnotation = AnnotationInstanceProvider.of(Inject.class);
builder.addToField(field, injectAnnotation);
pat.setAnnotatedType(builder.create());
}
}
}
private <X> boolean shouldInjectionAnnotationBeAddedToField(AnnotatedField<? super X> field) {
return !field.isAnnotationPresent(Inject.class) &&
field.isAnnotationPresent(PersistenceContext.class);
}
}
并在测试class
中产生合适的EntityManager
@RunWith(CdiRunner.class)
@AdditionalClasses(AddInjectToPersistenceContextInjectionsCdiExtension.class)
public class SampleServiceTest2 {
@Inject SampleService greeter;
EntityManagerFactory emf;
@PostConstruct
void init() {
emf = Persistence.createEntityManagerFactory("integration");
}
@Produces
EntityManager createEntityManager() {
return emf.createEntityManager();
}
@Test
public void testGreeter() throws Exception {
}
}
它并不完全等同于 Java EE 容器所做的,但它通常足够接近。
这是单元测试代码。当我们运行 单元测试代码(SampleServiceTest2);在 AbstractDao 中注入的 EntityManager 总是空的!我们如何在单元测试期间注入 em。
*** SampleServiceTest2.java
import javax.inject.Inject;
import org.jglue.cdiunit.CdiRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(CdiRunner.class)
public class SampleServiceTest2 {
@Inject SampleService greeter;
@Test
public void testGreeter() throws Exception {
System.out.println("before2");
greeter.addSampleData(new SampleDataDto(), new KullaniciDto());
System.out.println("after2");
}
}
*** SampleService.java
import javax.ejb.Stateless;
import javax.inject.Inject;
....
@Stateless
@SecuredBean
public class SampleService {
@Inject
SampleLogic sampleLogic;
@Yetki(tag="perm_add_sample_data")
public void addSampleData(SampleDataDto data, KullaniciDto aktifKullaniciDto){
SampleDataHelper sampleDataHelper = new SampleDataHelper();
SampleData sampleData = sampleDataHelper.getEntity(data);
KullaniciHelper kullaniciHelper = new KullaniciHelper();
Kullanici kullanici = kullaniciHelper.getEntity(aktifKullaniciDto);
sampleLogic.addData(sampleData, kullanici);
}
}
**** SampleLogic.java
import javax.inject.Inject;
....
public class SampleLogic {
@Inject
SampleDataDao sampleDataDao;
public void addData(SampleData data, Kullanici kullanici) {
addData1(data,kullanici);
System.out.println("SampleLogic : addData() called!");
}
public void addData1(SampleData data, Kullanici kullanici) {
sampleDataDao.create(data, kullanici);
}
}
**** SampleDataDao.java
public class SampleDataDao extends AbstractDao<SampleData> {
private static final long serialVersionUID = 1L;
}
**** AbstractDao.java
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
private static final long serialVersionUID = 1L;
@PersistenceContext(unitName="meopdb")
private EntityManager em;
protected EntityManager getEm() {
return em;
}
@SuppressWarnings("rawtypes")
private Class entityClass;
@SuppressWarnings("rawtypes")
private Class getEntityClass() {
if (entityClass == null) {
entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
return entityClass;
}
public T create(T t, Kullanici kullanici) {
if (t.getId() != null) {
throw new IllegalStateException("Create Operation: Oid should be null");
}
t.setId(getSeqNextValue(t));
t.setEklemeZamani(new Timestamp(Calendar.getInstance().getTimeInMillis()));
t.setEkleyenKullaniciId(kullanici.getId());
t.setDurumId(EnumDurum.AKTIF.getValue());
t = em.merge(t);
em.flush();
return t;
}
}
如果您使用 CDIUnit 进行测试,您得到的唯一结果是 CDI 注入,而不是 Java EE 的全部功能。使用 @PersistenceContext
将 entityManager 注入 AbstractDAO
不是独立 CDI 的一部分,仅当应用程序在 Java EE 应用程序服务器中 运行ning 时才受支持。
解决方法是使用CDI机制注入EntityManager,创建生产者。然后可以将生产者切换为单元测试中的替代方案以提供测试 entityManager。但是,在独立单元测试中设置 JPA 并不是那么简单,因为您需要直接在 persistence.xml 文件中指定连接属性。另外,不要忘记将 JPA 实现(hibernate、eclipselink)的依赖项添加到测试依赖项中。
但是,如果您不想调整应用程序的代码,或者您在测试中需要的不仅仅是 CDI,您应该看看 Arquillian Java EE test framework。
这是 CDIUnit 的示例:
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
...
@Inject
@Named("meopdb")
private EntityManager em;
...
}
// producer in application - just a wraper over `@PersisteneContext`
public class EntityManagerProducer {
@Produces
@PersistenceContext(unitName="meopdb")
@Named("meopdb")
private EntityManager em;
}
/* producer in your test sources - it creates entityManager via API calls instead of injecting via `@PersistenceContext`. Also, a different persistence unit is used so that it does not clash with main persistence unit, which requires datasource from app server
*/
public TestEntityManagerProducer {
@Produces
@ProducesAlternative // CDIUnit annotation to turn this on as an alternative automatically
@Named("meopdb")
public EntityManager getEm() {
return Persistence
.createEntityManagerFactory("meopdb-test")
.createEntityManager();
}
}
这还不够。您需要使用名为 "meopdb-test" 的测试持久性单元在您的测试资源中创建一个新的 persistence.xml
。对于此单元,您需要指定 RESOURCE_LOCAL
transaction-type
,并指定连接信息。最后不要忘记 - 您需要在 persistence.xml 或外部 orm 文件中列出所有实体。这是因为您的测试 运行 在应用程序服务器之外。在应用服务器内部,JPA 可以自动找到实体。
正如@OndroMih 所说,在 CDI-Unit 中,您唯一得到的就是 CDI 注入。所以你要作弊一点。
您可以使用扩展为所有 @PersistenceContext
注入添加 javax.inject.Inject
注释
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.*;
import javax.inject.Inject;
import javax.persistence.PersistenceContext;
import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
public class AddInjectToPersistenceContextInjectionsCdiExtension implements Extension {
<T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
Set<AnnotatedField<? super T>> fields = pat.getAnnotatedType().getFields();
for (AnnotatedField<? super T> field : fields) {
if (shouldInjectionAnnotationBeAddedToField(field)) {
AnnotatedType<T> at = pat.getAnnotatedType();
AnnotatedTypeBuilder<T> builder = new AnnotatedTypeBuilder<T>().readFromType(at);
Inject injectAnnotation = AnnotationInstanceProvider.of(Inject.class);
builder.addToField(field, injectAnnotation);
pat.setAnnotatedType(builder.create());
}
}
}
private <X> boolean shouldInjectionAnnotationBeAddedToField(AnnotatedField<? super X> field) {
return !field.isAnnotationPresent(Inject.class) &&
field.isAnnotationPresent(PersistenceContext.class);
}
}
并在测试class
中产生合适的EntityManager
@RunWith(CdiRunner.class)
@AdditionalClasses(AddInjectToPersistenceContextInjectionsCdiExtension.class)
public class SampleServiceTest2 {
@Inject SampleService greeter;
EntityManagerFactory emf;
@PostConstruct
void init() {
emf = Persistence.createEntityManagerFactory("integration");
}
@Produces
EntityManager createEntityManager() {
return emf.createEntityManager();
}
@Test
public void testGreeter() throws Exception {
}
}
它并不完全等同于 Java EE 容器所做的,但它通常足够接近。