EntityManager имеет значение null при модульном тестировании с помощью org.jglue.cdi-unit

Вот код модульного тестирования. Когда мы запускаем код модульного теста (SampleServiceTest2); EntityManager, введенный в AbstractDao, всегда равен нулю! Как мы можем ввести их во время модульного тестирования.

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

1 ответ

Решение

Если вы тестируете с помощью CDIUnit, вы получаете только инъекции CDI, а не всю мощь Java EE. Внедрение entityManager с помощью @PersistenceContext в AbstractDAO не является частью автономного CDI, он поддерживается только в том случае, если приложение выполняется на сервере приложений Java EE.

Решение состоит в том, чтобы внедрить EntityManager с использованием механизма CDI и создать производителя. Затем производитель может быть переключен на альтернативу в модульных тестах, чтобы обеспечить тестирование entityManager. Однако настроить JPA в автономном модульном тесте не так просто, так как вам нужно указать свойства соединения непосредственно в файле persistence.xml. Также не забудьте добавить зависимости от реализации JPA (hibernate, eclipselink) в свои тестовые зависимости.

Однако, если вы не хотите адаптировать код своего приложения или вам нужно больше, чем CDI в ваших тестах, вам стоит взглянуть на тестовую среду Arquillian Java EE.

Вот пример для 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();
    }

}

И этого еще недостаточно. Вам нужно создать новый persistence.xml в ваших ресурсах тестирования с модулем персистентности теста с именем "meopdb-test". Для этого блока необходимо указать RESOURCE_LOCALtransaction-typeи укажите информацию о соединении. И последнее, что не следует забывать - вам нужно перечислить все ваши сущности в файле persistence.xml или во внешнем файле orm. Это потому, что ваши тесты выполняются за пределами сервера приложений. Внутри сервера приложений JPA может автоматически находить объекты.

Как сказал @OndroMih, в CDI-Unit вы получаете только инъекции CDI. Так что вам придется немного обмануть.

Вы можете использовать расширение добавить javax.inject.Inject аннотации ко всем @PersistenceContext вливания

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

и производить подходящие 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, но это достаточно часто, чем нет.

Другие вопросы по тегам