如何在没有过于复杂的代码的情况下为Adapter编写JUnit?
我有一个从 I1
到 ILogger
的适配器,如下实现:
class BAdapter() implements I1
{
void logA() { // nothing }
void logB() { new BLogger().log() }
void logC() { // nothing }
}
我想编写 JUnit 测试来验证功能,但我发现它有点问题,因为我不能注入我的 Mock 对象而不是 BLogger,或验证返回值。我找到了几种可能的解决方案,但我不确定哪个是最好的。
案例一: 将 void setLogger(Logger l)
添加到 BAdapter
类。
class BAdapter() implements I1
{
private Logger logger = new BLogger();
public void logB() { logger.log() }
public void setLogger(Logger l) { logger = l }
.. //rest of methods
}
缺点:为什么要添加在“真实”的非测试代码中从未使用过的设置器?
情况二: 在测试包中添加受保护的工厂方法和子类BAdapter
。
class BAdapter() implements I1
{
public void logB() { createLogger().log() }
protected Logger createLogger() { retrun new BLogger() }
.. //rest of methods
}
class BAdapterForTesting extends BAdapter()
{
protected Logger createLogger() { retrun new MockBLogger() }
}
缺点:我不确定这是否是干净而优雅的解决方案,但我在这里没有看到太多缺点。
情况三: 使用抽象工厂模式。
class BAdapter() implements I1
{
public void logB() { AbstractFactory.getFactory().getBLogger().log() }
.. //rest of methods
}
在测试中的某个地方:
AbstractFactory.setFactory(new MockLoggersFactory())
缺点:这太复杂了,不是吗?
案例四: 例如,在执行日志记录时返回布尔值。例如
class BAdapter() implements I1
{
Boolean logA() { return false; }
Boolean logB() { return new BLogger().log() }
Boolean logC() { return false; }
}
缺点:这是一种解决方法。当没有人在“真实”的非测试代码中需要它时,为什么要返回一些值?
更好的解决方案? 有更好的吗?
I have an adapter from I1
to ILogger
implemented like this:
class BAdapter() implements I1
{
void logA() { // nothing }
void logB() { new BLogger().log() }
void logC() { // nothing }
}
I would like to write JUnit test, that verify the functionality, but I found it a bit problematic, since I cannot inject my Mock object instead of BLogger
, or verify return value. I found several possible solution, but I am not sure, which is the best.
Case One:
Add void setLogger(Logger l)
to the BAdapter
class.
class BAdapter() implements I1
{
private Logger logger = new BLogger();
public void logB() { logger.log() }
public void setLogger(Logger l) { logger = l }
.. //rest of methods
}
Cons: Why to add setter which is never used in "real", non-testing code?
Case Two:
Add protected factory method and sublcass BAdapter
in test package.
class BAdapter() implements I1
{
public void logB() { createLogger().log() }
protected Logger createLogger() { retrun new BLogger() }
.. //rest of methods
}
class BAdapterForTesting extends BAdapter()
{
protected Logger createLogger() { retrun new MockBLogger() }
}
Cons: I am not sure, if this is clean and elegant solution, but I don't see much cons here.
Case Three:
Use Abstract Factory pattern.
class BAdapter() implements I1
{
public void logB() { AbstractFactory.getFactory().getBLogger().log() }
.. //rest of methods
}
And somewhere in tests:
AbstractFactory.setFactory(new MockLoggersFactory())
Cons: This is too complex, isn't it?
Case Four:
Return Boolean, for example, when logging was performed. E.g.
class BAdapter() implements I1
{
Boolean logA() { return false; }
Boolean logB() { return new BLogger().log() }
Boolean logC() { return false; }
}
Cons: This is kind of wourkaround. Why to return some value, when nobody needs it in "real", non-testing code?
Better Solution?
Is there anything better?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
从您的代码中很难准确说出被测类试图实现的目标,但是
我会选择案例一,但需要注意的是我也会在“真实”代码中使用注入。
注入的好处之一是它使类(在本例中为适配器)更可重用。强制
logB
方法始终委托给BLogger
实例会在编译时设置该行为。如果我希望适配器委托给另一种类型的 Logger,我就无法使用它,因此它的可重用性稍差。From your code it's hard to tell exactly what the class under test is trying to achieve but
I'd go with Case One with the caveat that I'd use injection in the 'real' code too.
One of the benefits of injection is it makes the class, in this case your adapter, more re-usable. Forcing the
logB
method to always delegate to an instance ofBLogger
sets that behaviour in stone at compile time. If I want the adapter to delegate to another type ofLogger
I can't use it and it is therefore slightly less re-usable.IMVHO 创建专门用于测试而不是其他地方的代码并不是一个好主意,因为它可能会暴露不应该使用的功能,但有些人可能会认为使用它很酷并且感到非常惊讶。
这给我们留下了案例 3,这通常是一个非常好的方法,但如果它只是在测试期间使用,它仍然有点矫枉过正。
我建议使用一些额外的模拟框架,例如 PowerMock,它可以以方便的方式更改类中的私有字段:
然后在测试过程中将该字段交换为一些模拟:
更多信息:
http://code.google.com/p/powermock/wiki/BypassEncapsulation
模拟框架是测试过程中的重要资产,因此了解它们会有很大帮助。
IMVHO creating code especially to be used by tests and nowhere else is not a good idea as it may expose functionality that is not supposed to be used, yet some may think using it is cool and be badly surprised.
This leaves us with case 3 which is a generally very good approach, yet if it is only to be used during testing its still a bit of overkill.
I'd suggest using some additional mocking framework like PowerMock which can change private fields in classes in convenient way:
and then during testing swap the field to some mock:
more info:
http://code.google.com/p/powermock/wiki/BypassEncapsulation
Mocking frameworks are great asset during testing so knowing them helps a lot.