在测试期间注入具有@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 {
        // ...
    }
}