【发布时间】:2019-04-18 05:19:35
【问题描述】:
我有一个类似的界面:
public interface DateTimeService {
ZonedDateTime now();
void fixTo(ZonedDateTime date);
void forget();
}
我有两个实现。一种用于fixTo 和forget 抛出异常的生产环境,另一种用于测试我们控制时间的地方。然后我有一个 CDI 配置,它根据标志实例化正确的类型。
@ApplicationScoped
public class Configuration {
@Produces
@ApplicationScoped
public DateTimeService dateTimeService(Configuration config) {
if (config.isFakeDateTimeServiceEnabled()) {
return new FakeDateTimeService();
} else {
return new DefaultDateTimeService();
}
}
}
但是,我想从DateTimeService 中删除fixTo 和forget,因为它们只是在那里,所以我们可以控制测试时间。我制作了一个新界面,例如:
public interface FakeDateTimeService extends DateTimeService {
// fixto and forget is moved from DateTimeService to here
}
我有几个注入点。有些在生产代码中,有些在测试代码中。在生产代码中我希望只能访问DateTimeSerice,在测试代码中我希望能够处理扩展服务。
在产品代码中:
@Inject
private DateTimeService dateTimeService;
在测试代码中:
@Inject
private FakeDateTimeService dateTimeService;
如果我保持配置不变,那么测试代码将永远找不到我的扩展服务(因为 CDI 似乎忽略了生产者方法生成的实例的运行时类型)。
如果我更新配置以实例化两者(在这种情况下,我什至可以将正版服务注入假服务),然后生产无法连接在一起,因为假的也实现了DateTimeService 接口,它会导致歧义。在这一点上,我可能只使用限定符,但我既不想因此而更改我的生产代码,也不想在生产环境中存在虚假的日期时间服务。
我试图否决 bean 创建,但我没有这样做。
我认为会/应该工作的是实例化正确的类型并以编程方式将其添加到 bean 上下文中,但我发现的示例是针对 CDI 2.0 而现在,我的代码的相关部分停留在 CDI 1.2 上。
此时您可能可以看出,我不是 CDI 专家。因此,我愿意就良好的 CDI 阅读材料以及有关此问题的具体建议提出建议。
否则,我将要放弃,只使用具有 fixTo 和 forget 方法的 DateTimeService。
【问题讨论】:
-
CDI 使用类型安全解析,这里的重要一点是 bean 类型 - 您的
FakeDateTimeService作为 bean 实际上具有类型{FakeDateTimeService, DateTimeService, Object}(以及任何其他扩展类或实现的接口)。因此,它有资格注入这些类型的任何注入点,因此存在模棱两可的解析异常。如果我正确理解了您的意图,那么您可能想阅读 CDI Alternatives 并将FakeDateTimeService作为测试的启用替代品,因此在测试中的所有注入点替换DateTimeService。 -
其他方法可能是有一个 CDI 扩展并否决原始类型并注册一个新 bean。对于 CDI 1.2,这会稍微复杂一些,而且扩展只需要用于测试。我宁愿寻找一种方法来使这项工作与替代品一起使用。
-
绝对选择仅在测试类路径中可见的替代方案(或者可能是专家)。在生产中使用激活虚拟组件的开关让我感到害怕,因为一个错误可能会使生产不稳定或受到损害。如果你引用的测试代码只是单元测试,你甚至可以模拟
DateTimeService。 -
谢谢你们。我明白为什么模棱两可,我只是不知道下一步该去哪里。我会看看两种建议的解决方案。祝你有美好的一天!
-
您可能想看看ioc-unit。通过定义单独的测试代码和生产(sut)代码,很容易隐式定义 FakeDateTimeService 的优先级而无需替代