Injection of @PersistenceContext in CDI-Unit

时光怂恿深爱的人放手 提交于 2019-12-03 08:52:30

If you test with CDIUnit, the only thing you get is CDI injections, not the full power of Java EE. Injecting entityManager using @PersistenceContext into AbstractDAO is not part of standalone CDI, it is only supported when application is running within a Java EE application server.

The solution is to inject EntityManager using CDI mechanism and create a producer. The producer could be then switched for an alternative in unit tests to provide test entityManager. However, setting up JPA in a standalone unit test is not so straightforward, as you need to specify connection properties directly in persistence.xml file. Also, do not forget to add dependencies on a JPA implementation (hibernate, eclipselink) into your test dependencies.

However, if you do not want to adapt your application's code or you need more than CDI in your tests, you should have a look at Arquillian Java EE test framework.

Here is an example for 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();
    }

}

And it is not yet enough. You need to create a new persistence.xml in your test resources with the test persistence unit named "meopdb-test". For this unit you need to specify RESOURCE_LOCAL transaction-type, and specify connection information. And last thing not to forget - you need to list all your entities in the persistence.xml, or in external orm file. This is because your tests run outside of application server. Inside app server, JPA can find entities automatically.

As @OndroMih said, in CDI-Unit, the only thing you get is CDI injections. So you have to cheat a little.

You can use extension do add javax.inject.Inject annnotation to all @PersistenceContext injections

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);
    }
}

and produce suitable EntityManager in test class

@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  {

    }
}

It's not exactly equivalent of what Java EE container does, but it's close enough more often than not.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!