运行 在 Java SE 上进行单元测试时,使用 CDI 和 Weld 的不满足依赖关系
Unsatisfied dependencies using CDI with Weld when running unit tests on Java SE
我有一个小的 (Java SE 11.x
) 项目,我在其中使用 Weld 测试 CDI。我有这些依赖项(除其他外,但这些是与此问题相关的):
implementation("org.jboss.weld.se:weld-se-core:4.0.0.Final")
runtimeOnly("javax:javaee-api:8.0.1")
testImplementation("org.jboss.weld:weld-junit5:2.0.2.Final")
然后我有一个简单的实体class:
@Entity
@Table(name = "spell_books")
public final class SpellBook extends AbstractPersistable {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(name = "id", nullable = false, updatable = false)
private UUID id;
@Column(name = "name")
private String name;
// ...some one-to-many and many-to-may relationships
public SpellBook() { // No-args constructor required by JPA
spells = new LinkedList<>();
wizards = new LinkedList<>();
}
public SpellBook(final String name) {
this();
this.name = name;
}
public void add(final Spell spell) {
spell.setSpellBook(this);
spells.add(spell);
}
}
@MappedSuperclass
public abstract class AbstractPersistable implements Serializable {
@Version
@Column(name = "version")
private Long version;
}
...和一个 DAO class 与数据库接口:
import jakarta.inject.Singleton;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Singleton
public class SpellBookDao extends AbstractJpaDao<SpellBook> {
public Optional<SpellBook> findByName(final String name) {
logger.debug("Searching spell book with name [{}]...", name);
final var builder = entityManager.getCriteriaBuilder();
final var criteria = builder.createQuery(clazz);
final var model = criteria.from(clazz);
criteria.select(model).where(builder.equal(model.get("name"), name));
return Optional.ofNullable(entityManager.createQuery(criteria.select(model)).getSingleResult());
}
}
@Log4j2
public abstract class AbstractJpaDao<T extends AbstractPersistable> {
@PersistenceContext
protected EntityManager entityManager;
protected Class<T> clazz;
public void setClazz(final Class<T> clazz) {
this.clazz = clazz;
}
// ...some defaults for persist, merge, findAll, findOne, etc.
}
我想做的是为 DAO 编写一个简单的单元测试 class:
import jakarta.inject.Inject;
import org.acme.service_layer.persistence.SpellBookDao;
import org.assertj.core.api.Assertions;
import org.jboss.weld.junit5.EnableWeld;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.UUID;
@EnableWeld
final class SpellBookDaoTest {
@Inject
private SpellBookDao dao;
@Test
void findByName_WhenSpellBookExists() {
final var optional = dao.findByName("The Dark Lord Ascending");
Assertions.assertThat(optional)
.hasValueSatisfying(it -> {
Assertions.assertThat(it.getId()).isEqualTo(UUID.fromString("715811c9-ae11-41ec-8652-671fd88cd2a0"));
Assertions.assertThat(it.getName()).isEqualTo("The Dark Lord Ascending");
Assertions.assertThat(it.getSpells()).isEmpty();
Assertions.assertThat(it.getWizards()).isEmpty();
// ...
Assertions.assertThat(it.getVersion()).isEqualTo(0L);
});
}
}
此堆栈跟踪总是失败:
WELD-001408: Unsatisfied dependencies for type SpellBookDao with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao
at org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao(SpellBookDaoTest.java:0)
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type SpellBookDao with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao
at org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao(SpellBookDaoTest.java:0)
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:378)
...many more here
我已经搜索了很多类似的 questions/answers 但是,我仍然无法弄清楚我在这里做错了什么。有什么建议吗?
顺便说一句,我有一个 main/resources/META-INF/beans.xml
和 test/resources/META-INF/beans.xml
。两者的内容相同:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="all">
<scan>
<exclude name="org.jboss.weld.**" />
</scan>
</beans>
我认为,如果您至少没有使用范围注解对其中一个进行注解,则 CDI 无法提供实现。
将@ApplicationScoped 或@Singleton 放在SpellBookDao
刚看到您已经设置了应该有效的 bean-discovery-mode="all"...
[编辑]
Add to your gradle
test.doFirst {
copy {
from 'build/resources/main/META-INF/beans.xml'
into 'build/classes/main/META-INF/'
}
copy {
from 'build/resources/test/META-INF/beans.xml'
into 'build/classes/test/META-INF/'
}
}
来自
更改您的测试:
@ExtendWith(WeldJunit5Extension.class)
final class SpellBookDaoTest {
@WeldSetup
WeldInitiator weldInitiator = WeldInitiator.of(SpellBookDao.class);
在 gralde 配置中添加对 API 的依赖:
` testImplementation("javax:javaee-api:8.0.1") ``
您的 bean 将会被发现。
您现在必须提供 EntityManager。
文档在这里
https://github.com/weld/weld-junit/blob/master/junit5/README.md
https://weld.cdi-spec.org/news/2017/12/19/weld-meets-junit5/
正如我在 GitHub issue 中针对此问题所述 - 这是一个问题,因为截至今天 (23/02/2021),weld-junit
版本 2.x
已设置为有效使用 Java/Jakarta EE 8。我需要为 Jakarta EE 9 及其由 Weld 4 实现的 jakarta
命名空间发布一个新的主要版本。它应该不会太难,我希望会能够在一两周内找到时间。
编辑: weld-junit 版本 3.0.0.Final 现已在 Central 中可用,旨在与 Jakarta EE 9 命名空间一起使用,因此上述问题应该有效解决了。
到那时,如果您交换到具有 javax
命名空间的 EE 8,并且还使用实现该命名空间的 Weld 3,则只能使用 weld-junit
。
最后但并非最不重要的一点是,您的项目混合了 EE 8 和 EE 9 工件 - 这是您需要自己解决的问题,主要是由于两者之间的命名空间差异,并且即使在之后也会继续引起问题我们修复 weld-junit
.
我有一个小的 (Java SE 11.x
) 项目,我在其中使用 Weld 测试 CDI。我有这些依赖项(除其他外,但这些是与此问题相关的):
implementation("org.jboss.weld.se:weld-se-core:4.0.0.Final")
runtimeOnly("javax:javaee-api:8.0.1")
testImplementation("org.jboss.weld:weld-junit5:2.0.2.Final")
然后我有一个简单的实体class:
@Entity
@Table(name = "spell_books")
public final class SpellBook extends AbstractPersistable {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(name = "id", nullable = false, updatable = false)
private UUID id;
@Column(name = "name")
private String name;
// ...some one-to-many and many-to-may relationships
public SpellBook() { // No-args constructor required by JPA
spells = new LinkedList<>();
wizards = new LinkedList<>();
}
public SpellBook(final String name) {
this();
this.name = name;
}
public void add(final Spell spell) {
spell.setSpellBook(this);
spells.add(spell);
}
}
@MappedSuperclass
public abstract class AbstractPersistable implements Serializable {
@Version
@Column(name = "version")
private Long version;
}
...和一个 DAO class 与数据库接口:
import jakarta.inject.Singleton;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Singleton
public class SpellBookDao extends AbstractJpaDao<SpellBook> {
public Optional<SpellBook> findByName(final String name) {
logger.debug("Searching spell book with name [{}]...", name);
final var builder = entityManager.getCriteriaBuilder();
final var criteria = builder.createQuery(clazz);
final var model = criteria.from(clazz);
criteria.select(model).where(builder.equal(model.get("name"), name));
return Optional.ofNullable(entityManager.createQuery(criteria.select(model)).getSingleResult());
}
}
@Log4j2
public abstract class AbstractJpaDao<T extends AbstractPersistable> {
@PersistenceContext
protected EntityManager entityManager;
protected Class<T> clazz;
public void setClazz(final Class<T> clazz) {
this.clazz = clazz;
}
// ...some defaults for persist, merge, findAll, findOne, etc.
}
我想做的是为 DAO 编写一个简单的单元测试 class:
import jakarta.inject.Inject;
import org.acme.service_layer.persistence.SpellBookDao;
import org.assertj.core.api.Assertions;
import org.jboss.weld.junit5.EnableWeld;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.UUID;
@EnableWeld
final class SpellBookDaoTest {
@Inject
private SpellBookDao dao;
@Test
void findByName_WhenSpellBookExists() {
final var optional = dao.findByName("The Dark Lord Ascending");
Assertions.assertThat(optional)
.hasValueSatisfying(it -> {
Assertions.assertThat(it.getId()).isEqualTo(UUID.fromString("715811c9-ae11-41ec-8652-671fd88cd2a0"));
Assertions.assertThat(it.getName()).isEqualTo("The Dark Lord Ascending");
Assertions.assertThat(it.getSpells()).isEmpty();
Assertions.assertThat(it.getWizards()).isEmpty();
// ...
Assertions.assertThat(it.getVersion()).isEqualTo(0L);
});
}
}
此堆栈跟踪总是失败:
WELD-001408: Unsatisfied dependencies for type SpellBookDao with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao
at org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao(SpellBookDaoTest.java:0)
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type SpellBookDao with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao
at org.acme.service_layer.persistence.internal.SpellBookDaoTest.dao(SpellBookDaoTest.java:0)
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:378)
...many more here
我已经搜索了很多类似的 questions/answers 但是,我仍然无法弄清楚我在这里做错了什么。有什么建议吗?
顺便说一句,我有一个 main/resources/META-INF/beans.xml
和 test/resources/META-INF/beans.xml
。两者的内容相同:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="all">
<scan>
<exclude name="org.jboss.weld.**" />
</scan>
</beans>
我认为,如果您至少没有使用范围注解对其中一个进行注解,则 CDI 无法提供实现。 将@ApplicationScoped 或@Singleton 放在SpellBookDao
刚看到您已经设置了应该有效的 bean-discovery-mode="all"...
[编辑]
Add to your gradle
test.doFirst {
copy {
from 'build/resources/main/META-INF/beans.xml'
into 'build/classes/main/META-INF/'
}
copy {
from 'build/resources/test/META-INF/beans.xml'
into 'build/classes/test/META-INF/'
}
}
来自
更改您的测试:
@ExtendWith(WeldJunit5Extension.class)
final class SpellBookDaoTest {
@WeldSetup
WeldInitiator weldInitiator = WeldInitiator.of(SpellBookDao.class);
在 gralde 配置中添加对 API 的依赖: ` testImplementation("javax:javaee-api:8.0.1") ``
您的 bean 将会被发现。 您现在必须提供 EntityManager。
文档在这里 https://github.com/weld/weld-junit/blob/master/junit5/README.md https://weld.cdi-spec.org/news/2017/12/19/weld-meets-junit5/
正如我在 GitHub issue 中针对此问题所述 - 这是一个问题,因为截至今天 (23/02/2021),weld-junit
版本 2.x
已设置为有效使用 Java/Jakarta EE 8。我需要为 Jakarta EE 9 及其由 Weld 4 实现的 jakarta
命名空间发布一个新的主要版本。它应该不会太难,我希望会能够在一两周内找到时间。
编辑: weld-junit 版本 3.0.0.Final 现已在 Central 中可用,旨在与 Jakarta EE 9 命名空间一起使用,因此上述问题应该有效解决了。
到那时,如果您交换到具有 javax
命名空间的 EE 8,并且还使用实现该命名空间的 Weld 3,则只能使用 weld-junit
。
最后但并非最不重要的一点是,您的项目混合了 EE 8 和 EE 9 工件 - 这是您需要自己解决的问题,主要是由于两者之间的命名空间差异,并且即使在之后也会继续引起问题我们修复 weld-junit
.