我已经开始使用Guice对项目进行一些依赖注入,主要是因为我需要从单元测试中注入一层(当前使用JMock)一层,这使得手动注入非常笨拙。
我的问题是引入模拟的最佳方法是什么?我目前所拥有的是在单元测试中创建一个满足依赖关系的新模块,并将它们绑定到如下所示的提供者:
public class JMockProvider<T> implements Provider<T> {
private T mock;
public JMockProvider(T mock) {
this.mock = mock;
}
public T get() {
return mock;
}
}
在构造函数中传递mock,因此JMock设置可能如下所示:
final CommunicationQueue queue = context.mock(CommunicationQueue.class);
final TransactionRollBack trans = context.mock(TransactionRollBack.class);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(CommunicationQueue.class).toProvider(new JMockProvider<QuickBooksCommunicationQueue>(queue));
bind(TransactionRollBack.class).toProvider(new JMockProvider<TransactionRollBack>(trans));
}
});
context.checking(new Expectations() {{
oneOf(queue).retrieve(with(any(int.class)));
will(returnValue(null));
never(trans);
}});
injector.getInstance(RunResponse.class).processResponseImpl(-1);
有更好的方法吗?我知道AtUnit试图解决这个问题,虽然我错过了它如何自动神奇地注入像上面那样在本地创建的模拟,但我正在寻找一个令人信服的理由,为什么AtUnit在这里是正确的答案(其他而不是在不改变测试的情况下改变DI和模拟框架的能力,或者是否有更好的解决方案来手工完成。
答案 0 :(得分:12)
你不应该通过DI框架注入模拟。我非常成功地使用Guice和JMock,我的单元测试没有引用与Guice相关的任何内容。我只使用模拟并传入适用的null
。
DI将允许注入和构造当前类的依赖项,因此如果要添加一个模拟的类(有效地停止依赖图),您只需要将其传入.Misko Hevery在其中一个中声明谷歌技术会谈单元测试应该充满new
和null
,因为它们的范围已经局限于单个单元测试方法 - 我必须同意他的看法。
是否有理由需要在测试中使用Guice,即它们是否不是功能/集成测试?
如果排除注射器,测试是否会起作用?难道你不能将你的测试重构为:
final CommunicationQueue queue = context.mock(CommunicationQueue.class);
final TransactionRollBack trans = context.mock(TransactionRollBack.class);
context.checking(new Expectations() {{
oneOf(queue).retrieve(with(any(int.class)));
will(returnValue(null));
never(trans);
}});
RunResponse r = new RunResponse(queue, trans); // depending on the order
r.processResponseImpl(-1);
答案 1 :(得分:1)
根据你的说法,听起来你没有进行真正的单元测试。在为类进行单元测试时,您只关注单个类。执行单元测试时,执行应仅执行要测试的主题类,并避免从相关类中运行方法。
JMock / Easymock是帮助您实现单元测试的工具。它们在集成测试中没用。
在单元测试的背景下,Guice与JMock / Easymock重叠。 Guice强制用户使用所有类型的注释(例如@Inject)污染他们的代码,以获得与JMock / Easymock相同的结果。我非常强烈反对Guice帮助测试的方式。如果开发人员发现自己使用的是Guice而不是JMock / Easymock,那么他们要么不进行真正的单元测试,要么尝试测试控制器类。
测试控制器类不应该在单元测试级别进行,而是在集成测试级别进行。对于集成测试,使用Jakarta Cactus非常有帮助。在集成测试中,开发人员应该拥有所有依赖项,因此不需要使用Guice。
作为结论,我认为没有任何理由使用Guice来测试产品。 Google Guice可能在不同的环境中有用,但需要测试。
答案 2 :(得分:0)
Yishai,我必须纠正推动更好的设计应归功于Martin Fowler引入的依赖注入模式。如果您的代码已经遵循DI模式,Guice是一个帮助的工具。 Guice对您更好的编码没有任何贡献。 Guice正在以临时方式(在注释级别)解决DI,因此不应将其视为DI框架。这是Spring的基于xml的DI容器和Guice基于注释的DI容器之间的最大区别。
我花了一些时间来阅读你在开始时发布的代码(我通常不会),只是为了理解为什么你说单元测试是不可能的。我终于明白了你所面临的问题。从我所看到的单元测试级别来看,当你在Java EE服务器中运行时,你试图模仿代码段的执行流程。这就是你被卡住的原因。我不知道这里的激励是什么,但我担心你准备所有这些环境依赖的努力将被浪费掉:(
编写单元测试是测试“业务逻辑”,它独立于您使用的任何技术。可能存在一项要求,即必须在服务质量(QoS)环境中执行此业务逻辑。该要求是“技术要求”。在您的示例中, CommunicationQueue 和 TransactionRollBack 主要用于满足“技术要求”。情况是你的“业务需求”被所有这些环境单位所包围。你应该做的是重构你的代码,以便你的“业务需求”在没有依赖关系的独立方法中。然后你为这些方法编写单元测试。
我仍然认为你正在进行功能测试。在单元测试级别进行功能测试非常耗时,也不值得做这样的事情。我希望它有所帮助
答案 3 :(得分:0)
Yishai,听起来你需要AOP来绕过这些依赖关系。 AspectJ和AspectWerkz都是有用的资源。但是,AspectWekz具有使用JVM热交换技术在运行时编织方面类的非常好的功能。当然,这些工程工作应仅适用于单元测试级别。
在进行单元测试时,我已经花了很多精力来解决依赖关系。诸如JMock,EasyMock,Java EE微容器,AspectJ,AspectWerkz,Spring IoC等工具可用于解决DI问题。这些工具都没有推动开发人员将测试意识纳入其生产代码中。太糟糕了,Guice违反了保持代码清除任何类型的测试意识的概念。我不认为它是在我的单元测试中使用的工具。
我希望我已经给你足够的理由将@Inject注释从生产代码中删除。