如何在没有过多复杂代码的情况下编写JUnit for Adapter?

时间:2011-07-28 14:27:34

标签: java junit mocking inversion-of-control adapter

我有一个从I1ILogger的适配器,如下所示:

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
}

缺点:为什么要添加从未在“真实”,非测试代码中使用过的setter?

案例二: 在测试包中添加受保护的工厂方法和sublcass 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())

缺点:这太复杂了,不是吗?

案例四: 返回布尔值,例如,执行日志记录时。 E.g。

class BAdapter() implements I1
{
   Boolean logA() { return false; }
   Boolean logB() { return new BLogger().log() }
   Boolean logC() { return false; }
}

缺点:这是一种wourkaround。当没有人在“真正的”非测试代码中需要它时,为什么要返回一些值?

更好的解决方案? 还有什么更好的吗?

2 个答案:

答案 0 :(得分:2)

从你的代码中很难确切地说出被测试的类正在尝试实现什么 我会选择Case One,但我也会在“真正的”代码中使用注入。

注入的一个好处是它使类(在本例中为适配器)更易于重用。强制logB方法始终委托给BLogger的实例在编译时设置该行为。如果我希望适配器委托给另一种类型的Logger我不能使用它,因此它的可重用性稍差。

答案 1 :(得分:0)

IMVHO创建代码特别是供测试使用而不是其他地方不是一个好主意,因为它可能会暴露不应该使用的功能,但有些人可能认为使用它很酷并且非常惊讶。

这给我们留下了案例3,这是一种非常好的方法,但如果它只是在测试期间使用它仍然有点过分。

我建议使用一些额外的模拟框架,比如PowerMock,可以方便地更改类中的私有字段:

class BAdapter() implements I1
{
   private Logger logger = new BLogger();

   public void logB() { logger.log() }
   .. //rest of methods
}

然后在测试期间将字段交换为一些模拟:

Whitebox.setInternalState(loggerInstance, "logger", new MockLogger());

更多信息: http://code.google.com/p/powermock/wiki/BypassEncapsulation

模拟框架在测试过程中非常重要,因此了解它们有很大帮助。