模拟接口的廉价方法,没有运行时开销
假设我有一个包含很多方法的接口,我想模拟这些方法以进行测试,并且假设我不需要它执行任何操作,我只需要被测试的对象拥有它的实例。例如,我想对某些代码运行一些性能测试/基准测试,并且不希望此接口上的方法做出贡献。
有很多工具可以轻松做到这一点,例如
Interface mock = Mockito.mock(Interface.class);
ObjectUnderTest obj = ...
obj.setItem(mock);
等等。
然而,它们都会带来一些我宁愿避免的运行时开销:
- Mockito 记录所有调用,存储参数以供稍后验证
- JMock 和其他(我相信)要求您定义它们要做什么(没什么大不了的),然后执行通过各种代理来实际调用该方法。
- 好的旧 java.lang.reflect.Proxy 和朋友在到达要调用的方法之前都会在堆栈上至少再经历几次方法调用,通常是反射式的。
(我愿意对这些示例的任何细节进行纠正,但我相信原则是成立的。)
我的目标是接口的“真正的”无操作实现,例如我可以这样写处理所有返回 null
、false
或 0
的内容。但是,如果我感到懒惰并且界面有大量方法,那么这并没有帮助。那么,如何在运行时生成并实例化任意接口的这种无操作实现呢?
有一些可用的工具,例如 Powermock、CGLib 使用字节码生成,但仅作为更大的模拟/代理上下文的一部分,我还没有弄清楚要从内部挑选什么。
好的,所以这个例子可能有点做作,我怀疑代理会对时间产生太大的影响,但我现在很好奇如何生成这样一个类。 CGLib、ASM 容易吗?
编辑:是的,这是过早的优化,没有真正需要这样做。写完这个问题后,我认为最后一句话并没有完全表达我的观点,即我更感兴趣的是如何原则上做到这一点,以及动态类生成的简单方法,而不是我给出的实际用例。也许从一开始就措辞不好。
Suppose I have an interface with lots of methods that I want to mock for a test, and suppose that I don't need it to do anything, I just need the object under test to have an instance of it. For example, I want to run some performance testing/benchmarking over a certain bit of code and don't want the methods on this interface to contribute.
There are plenty of tools to do that easily, for example
Interface mock = Mockito.mock(Interface.class);
ObjectUnderTest obj = ...
obj.setItem(mock);
or whatever.
However, they all come with some runtime overhead that I would rather avoid:
- Mockito records all calls, stashing the arguments for verification later
- JMock and others (I believe) require you to define what they going to do (not such a big deal), and then execution goes through a proxy of various sorts to actual invoke the method.
- Good old java.lang.reflect.Proxy and friends all go through at least a few more method calls on the stack before getting to the method to be invoked, often reflectively.
(I'm willing to be corrected on any of the details of those examples, but I believe the principle holds.)
What I'm aiming for is a "real" no-op implementation of the interface, such as I could write by hand with everything returning null
, false
or 0
. But that doesn't help if I'm feeling lazy and the interface has loads of methods. So, how can I generate and instantiate such a no-op implementation of an arbitrary interface at runtime?
There are tools available such as Powermock, CGLib that use bytecode generation, but only as part of the larger mocking/proxying context and I haven't yet figured out what to pick out of the internals.
OK, so the example may be a little contrived and I doubt that proxying will have too substantial an impact on the timings, but I'm curious now as to how to generate such a class. Is it easy in CGLib, ASM?
EDIT: Yes, this is premature optimisation and there's no real need to do it. After writing this question I think the last sentence didn't quite make my point that I'm more interested in how to do it in principle, and easy ways into dynamic class-generation than the actual use-case I gave. Perhaps poorly worded from the start.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不确定这是否是您正在寻找的,但是 Eclipse 中的“新类”向导允许您构建一个新类并指定超类和/或接口。如果您允许,它将自动编码所有接口/抽象方法的虚拟实现(除非
void
,否则返回null
)。做起来非常轻松。我怀疑其他“大牌”IDE,例如 NetBeans 和 Idea,也有类似的设施。
编辑:
再次查看您的问题,我想知道为什么您在处理测试类时会担心自动代理的性能。在我看来,如果性能是一个问题,那么您应该测试“真正的”功能,并且如果您正在处理大多数未实现的类,那么您不应该处于性能很重要的测试情况。
Not sure if this is what you're looking for, but the "new class" wizard in Eclipse lets you build a new class and specify superclass and/or interface(s). If you let it, it will auto-code up dummy implementations of all interface/abstract methods (returning
null
unlessvoid
). It's pretty painless to do.I suspect the other "big name" IDEs, such as NetBeans and Idea, have similar facilities.
EDIT:
Looking at your question again, I wonder why you'd be concerned about performance of auto proxies when dealing with test classes. It seems to me that if performance is an issue, you should be testing "real" functionality, and if you're dealing with mostly-unimplemented classes anyway then you shouldn't be in a testing situation where performance matters.
构建该实用程序需要做一些工作,但对于没有“边缘情况”(注释等)的基本普通 Java 接口来说,使用 Javassist 代码生成在运行时以文本方式创建一个类来实现每个的空版本可能并不太难。接口上定义的方法。这与 Javassist ProxyFactory(或 CGLib Enhancer)代理对象不同,后者仍然具有几层间接层。我认为直接字节码生成模式生成的类不会产生任何开销。如果你足够勇敢,你也可以深入 ASM 来做同样的事情。
It would take a little work to build the utility, but probably not too hard for basic vanilla Java interface without "edge cases" (annotations, etc), to use Javassist code generation to textually create a class at runtime that implements null versions of every method defined on the interface. This would be different from Javassist ProxyFactory (Or CGLib Enhancer) proxy objects which would still have a few layers of indirection. I think there would be no overhead in the resulting class from the direct bytecode generation mode. If you are brave you could also dive into ASM to do the same thing.