在测试期间注入具有@Inject 的私有字段?
Injecting private fields having @Inject during test?
我需要为我的 Java class PartitionMapper
编写一个测试 PartitionMapperTest
。此 class 具有带有 @Inject
注释的私有字段,但它只有一个无参数构造函数。
为了测试,我想在任何测试之前创建一个 partitionMapper
并将值注入其私有字段。然后,测试人员测试映射器的方法 mapPartitions
并断言值。但是,我不知道如何将这些值注入 partitionMapper
.
PartitionMapper.java
@Named
public class PartitionMapper implements javax.batch.api.partition.PartitionMapper {
@Inject
private JobContext jobContext;
@Inject
@BatchProperty
private String fetchSize;
@Inject
@BatchProperty
private String rowsPerPartition;
// other batch properties ...
@PersistenceUnit(unitName = "h2")
private EntityManagerFactory emf;
@Override
public PartitionPlan mapPartitions() throws Exception {
// ...
}
}
PartitionMapperTest.java
public class PartitionMapperTest {
private PartitionMapper partitionMapper;
@Before
public void setUp() {
// Prepare JobContext, batch properties to inject ...
// Instantiation
partitionMapper = new PartitionMapper();
// TODO How to inject these objects into partitionMapper?
}
@Test
public void testMapPartitions() throws Exception {
PartitionPlan partitionPlan = partitionMapper.mapPartitions();
for (Properties p : partitionPlan.getPartitionProperties()) {
// Assertions here ...
}
}
// ...
}
我确实基于 Mockito 和 PowerMock 实现了一个真正的 PartitionMapperTest
,可以在我的 GitHub 上看到。问题是有太多的假设导致用户理解的代码非常差。我正在寻找重构它的另一种解决方案。
有没有理由只有一个无参数的构造函数?
我建议您使用构造函数注入而不是字段注入。那会解决你的问题。
例如代替:
public class Foo {
@Inject
private Bar bar;
}
这样做:
public class Foo {
private Bar bar;
public Foo(@Inject Bar bar) {
this.bar = bar;
}
}
如果您以这种方式定义注入点,您将拥有一个干净的 API 并且您的 class 可以在非 cdi 环境(如单元测试)中使用。
有很多关于 "constructor injection VS field injection" 的资源...也在 Whosebug 上,例如.
使用受保护字段而不是私有字段以便能够在单元测试中模拟字段:
@Named
public class PartitionMapper implements javax.batch.api.partition.PartitionMapper {
@Inject
JobContext jobContext;
@Inject
@BatchProperty
String fetchSize;
@Inject
@BatchProperty
String rowsPerPartition;
// other batch properties ...
@PersistenceUnit(unitName = "h2")
EntityManagerFactory emf;
@Override
public PartitionPlan mapPartitions() throws Exception {
// ...
}
}
我需要为我的 Java class PartitionMapper
编写一个测试 PartitionMapperTest
。此 class 具有带有 @Inject
注释的私有字段,但它只有一个无参数构造函数。
为了测试,我想在任何测试之前创建一个 partitionMapper
并将值注入其私有字段。然后,测试人员测试映射器的方法 mapPartitions
并断言值。但是,我不知道如何将这些值注入 partitionMapper
.
PartitionMapper.java
@Named
public class PartitionMapper implements javax.batch.api.partition.PartitionMapper {
@Inject
private JobContext jobContext;
@Inject
@BatchProperty
private String fetchSize;
@Inject
@BatchProperty
private String rowsPerPartition;
// other batch properties ...
@PersistenceUnit(unitName = "h2")
private EntityManagerFactory emf;
@Override
public PartitionPlan mapPartitions() throws Exception {
// ...
}
}
PartitionMapperTest.java
public class PartitionMapperTest {
private PartitionMapper partitionMapper;
@Before
public void setUp() {
// Prepare JobContext, batch properties to inject ...
// Instantiation
partitionMapper = new PartitionMapper();
// TODO How to inject these objects into partitionMapper?
}
@Test
public void testMapPartitions() throws Exception {
PartitionPlan partitionPlan = partitionMapper.mapPartitions();
for (Properties p : partitionPlan.getPartitionProperties()) {
// Assertions here ...
}
}
// ...
}
我确实基于 Mockito 和 PowerMock 实现了一个真正的 PartitionMapperTest
,可以在我的 GitHub 上看到。问题是有太多的假设导致用户理解的代码非常差。我正在寻找重构它的另一种解决方案。
有没有理由只有一个无参数的构造函数?
我建议您使用构造函数注入而不是字段注入。那会解决你的问题。
例如代替:
public class Foo {
@Inject
private Bar bar;
}
这样做:
public class Foo {
private Bar bar;
public Foo(@Inject Bar bar) {
this.bar = bar;
}
}
如果您以这种方式定义注入点,您将拥有一个干净的 API 并且您的 class 可以在非 cdi 环境(如单元测试)中使用。
有很多关于 "constructor injection VS field injection" 的资源...也在 Whosebug 上,例如.
使用受保护字段而不是私有字段以便能够在单元测试中模拟字段:
@Named
public class PartitionMapper implements javax.batch.api.partition.PartitionMapper {
@Inject
JobContext jobContext;
@Inject
@BatchProperty
String fetchSize;
@Inject
@BatchProperty
String rowsPerPartition;
// other batch properties ...
@PersistenceUnit(unitName = "h2")
EntityManagerFactory emf;
@Override
public PartitionPlan mapPartitions() throws Exception {
// ...
}
}