Тестовые репозитории DeltaSpike не могут внедрить EntityManager
Я пытаюсь запустить простой тест для хранилища DeltaSpike. Однако я не могу ввести Entity Manager. Я использую продюсер, который находится и тестирует источники:
@ApplicationScoped
public class EntityManagerProducer {
@Produces
public EntityManager getEntityManager() {
return Persistence
.createEntityManagerFactory("primary-test")
.createEntityManager();
}
}
В папке META-INF для тестируемых ресурсов у меня есть файл persistence.xml с определенным модулем персистентности основного теста:
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="primary-test" transaction-type="RESOURCE_LOCAL">
<properties>
<!-- Configuring JDBC properties -->
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test"/>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<!-- Hibernate properties -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.format_sql" value="false"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
В очень простом модуле я пытаюсь внедрить Entity Manager:
@RunWith(CdiTestRunner.class)
public class UserRepositoryTest {
@Inject
private EntityManager entityManager;
@Test
public void shouldReturnEmptyListWhenDBIsEmpty() {
// dummy test here
}
}
Однако, когда я запускаю его, я получаю исключение:
WELD-001408 Unsatisfied dependencies for type [EntityManager] with qualifiers [@Default] at injection point [[field] @Inject private com.repository.UserRepositoryTest.entityManager]
Мой pom.xml зависит от тестового модуля Apache DeltaSpike и Weld:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<deltaspike.version>1.8.2</deltaspike.version>
<weld.version>1.1.10.Final</weld.version>
</properties>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-test-control-module-api</artifactId>
<version>${deltaspike.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-test-control-module-impl</artifactId>
<version>${deltaspike.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-weld</artifactId>
<version>${deltaspike.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>${weld.version}</version>
<scope>test</scope>
</dependency>
Любые идеи, почему Entity Manager не может быть введен? Я не хочу использовать Arquillian.
РЕДАКТИРОВАТЬ: Текущий pom.xml:
<groupId>com.us</groupId>
<artifactId>deltaspike-samples</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>DeltaSpike samples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<deltaspike.version>1.8.1</deltaspike.version>
<weld.version>3.0.4.Final</weld.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.deltaspike.distribution</groupId>
<artifactId>distributions-bom</artifactId>
<version>${deltaspike.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.wildfly.bom</groupId>
<artifactId>jboss-javaee-7.0-with-hibernate</artifactId>
<version>8.2.2.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-data-module-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-data-module-impl</artifactId>
<scope>runtime</scope>
</dependency>
<!--Test dependencies-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-test-control-module-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-test-control-module-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-api</artifactId>
<version>${deltaspike.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-weld</artifactId>
<version>${deltaspike.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-shaded</artifactId>
<version>3.0.4.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.annotation</groupId>
<artifactId>jboss-annotations-api_1.2_spec</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2 ответа
Использование CDI в JAVA SE требует beans.xml
быть помещенным в META-INF
хотя это необязательно, так как Java EE 7.
Затем установите режим обнаружения на annotated
и ваш производитель должен быть обнаружен.
Вот рабочая конфигурация:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<deltaspike.version>1.8.2</deltaspike.version>
<weld.version>3.0.4.Final</weld.version>
</properties>
Теперь вот как вы настраиваете базовую зависимость DeltaSpike:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.deltaspike.distribution</groupId>
<artifactId>distributions-bom</artifactId>
<version>${deltaspike.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
В зависимости вы должны иметь следующие спецификации:
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
Теперь пришло время зависеть от реализаций.
Во-первых, сам Deltaspike:
<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-api</artifactId>
<scope>compile</scope>
</dependency>
Затем JBoss Weld 3 (CDI 2.0 вкл.)
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-shaded</artifactId>
<version>${weld.version}</version>
<scope>runtime</scope>
</dependency>
И сварочный контроллер для DeltaSpike:
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-weld</artifactId>
<scope>runtime</scope>
</dependency>
Затем модуль данных DeltaSpike:
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-data-module-api</artifactId>
<version>${deltaspike.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-data-module-impl</artifactId>
<version>${deltaspike.version}</version>
<scope>runtime</scope>
</dependency>
Теперь пришло время для реализации JPA (EclipseLink JPA 2.7.1) и встроенного сервера базы данных H2:
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>runtime</scope>
</dependency>
Чтобы создать модульный тест с JUnit 5, вам нужно:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
И чтобы иметь возможность запускать CDI с одной аннотацией в классе JUnit, вам также нужно это:
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-junit5</artifactId>
<version>1.2.2.Final</version>
<scope>test</scope>
</dependency>
Чтобы включить JUnit 5 в Maven, необходимо настроить maven-surefire-plugin
:
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.0.3</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
И я буду использовать Lombok и SLF4J:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
Вот моя структура проекта:
main/java/fr/fxjavadevblog
+-- VideoGame.java (JPA Entity)
+-- VideoGameFactory.java
+-- VideoGameRepository.java (interface)
+-- InjectedUUID.java (annotation def.)
+-- Producers.java (produces EntityManager and UUID has string)
/resources/META-INF
+-- beans.xml
+-- persistence.xml
test/java/fr/fxjavadevblog
+-- VideoGameReposityTest.java (JUnit 5)
/resources/META-INF
+-- beans.xml
+-- persistence.xml
Вот мои производители CDI, необходимые для DeltaSpike Data и для добавления UUID в качестве закрытых ключей:
package fr.fxjavadevblog;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import java.util.UUID;
/**
* composite of CDI Producers.
*
* @author robin
*/
@ApplicationScoped
public class Producers
{
public static final String UNIT_NAME = "cdi-deltaspike-demo";
/**
* produces the instance of entity manager for the application and for DeltaSpike.
*/
@Produces
@SuppressWarnings("unused") // just remove the warning, because the field serves as CDI Producer and the IDE cannot detect it.
private static EntityManager em = Persistence.createEntityManagerFactory(UNIT_NAME).createEntityManager();
/**
* produces randomly generated UUID for primary keys.
*
* @return UUID as a HEXA-STRING
*
*/
@Produces
@InjectedUUID
@SuppressWarnings("unused") // just remove the warning, because the method serves as CDI Producer and the IDE cannot detect it.
public String produceUUIDAsString()
{
return UUID.randomUUID().toString();
}
}
этот класс использует пользовательский CDI Qualifier @InjectedUUID
:
package fr.fxjavadevblog;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* CDI Qualifier for UUID Producers
*
* @author robin
*/
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface InjectedUUID
{
}
Вот моя сущность JPA, использующая аннотации Lombok и CDI:
package fr.fxjavadevblog;
import lombok.*;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.persistence.*;
import java.io.Serializable;
/**
* simple JPA Entity, using Lombok and CDI Injected fields (UUID).
*
* @author robin
*/
// lombok annotations
@NoArgsConstructor(access = AccessLevel.PROTECTED) // to avoid direct instanciation bypassing the factory.
@ToString(of = {"id","name"})
@EqualsAndHashCode(of="id")
// CDI Annotation
@Dependent
// JPA Annotation
@Entity
public class VideoGame implements Serializable {
@Id
@Inject @InjectedUUID // ask CDI to inject an brand new UUID
@Getter
private String id;
@Getter @Setter
private String name;
// this field will work as a flag to know if the entity has already been persisted
@Version
@Getter
private Long version;
}
Вот простая фабрика для моего класса сущности:
/**
* simple Factory for creation VideoGame instances populated with UUID, ready to persist.
* This factory is need to get a proper Entity. Entities must not be created with the "new" operator, but must be build
* by invoking CDI.
*
* @author robin
*/
public class VideoGameFactory
{
/**
* creates and brand new VideoGame instance with its own UUID as PK.
*
* @return instance of a VideoGame
*/
public static VideoGame newInstance()
{
// ask CDI for the instance, injecting required dependencies.
return CDI.current().select(VideoGame.class).get();
}
}
И последнее, но не менее важное: DeltaSpike Data Resposity для моей организации:
package fr.fxjavadevblog;
import org.apache.deltaspike.data.api.EntityRepository;
import org.apache.deltaspike.data.api.Repository;
/**
* CRUD (and much more) interface, using DeltaSpike Data module.
*
* @author robin
*/
@Repository
interface VideoGameRepository extends EntityRepository <VideoGame, String>
{
// nothing to code here : automatic Repo generated by DeltaSpike
}
Вот файлы конфигурации:
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="annotated" version="2.0">
</beans>
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="cdi-deltaspike-demo" transaction-type="RESOURCE_LOCAL">
<class>fr.fxjavadevblog.VideoGame</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test"/>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
</properties>
</persistence-unit>
</persistence>
Эти файлы также дублируются в папку test/resources/META-INF.
И, наконец, вот модульный тест:
package fr.fxjavadevblog;
import org.jboss.weld.junit5.EnableWeld;
import org.jboss.weld.junit5.WeldInitiator;
import org.jboss.weld.junit5.WeldSetup;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
import javax.inject.Inject;
/**
* simple test class for the VideoGameRepository, using LOMBOK and WELD.
*
* @author robin
*/
@Slf4j
@EnableWeld
class VideoGameRepositoryTest
{
@WeldSetup // This is need to discover Producers and DeltaSpike Repository functionality
private WeldInitiator weld = WeldInitiator.performDefaultDiscovery();
@Inject
private VideoGameRepository repo;
@Test
void test()
{
VideoGame videoGame = VideoGameFactory.newInstance();
videoGame.setName("XENON");
repo.save(videoGame);
// testing if the ID field had been generated by the JPA Provider.
Assert.assertNotNull(videoGame.getVersion());
Assert.assertTrue(videoGame.getVersion() > 0);
log.info("Video Game : {}", videoGame);
}
}
Использование Lombok и UUID-производителя не является обязательным.
Вы можете найти полный исходный код и клонировать репозиторий на github: https://github.com/fxrobin/cdi-deltaspike-demo
У меня были похожие проблемы с Deltaspike и интеграционным тестом Junit5 для хранилища базы данных. В принципе, я не мог внедрить хранилище при использовании Junit5 @Test
аннотаций. Я должен был использовать Junit4 @Test
аннотации как обходной путь.