JUnit学习笔记8---mock object进行独立测试2

    使用mock objects,重构将变得容易!

      有些人说过,单元测试应该对测试中的代码透明:你不应为了简化测试而改变运行时的代码。这是错误的!单元测试是对运行时代码最好的运用。应该同其他运用同等的看待。如果你的代码不能在测试中运用,你应该纠正代码。

       例如,你看下面的代码怎样?

[...]
 import java.util.PropertyResourceBundle;
 import java.util.ResourceBundle;;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 [...]
  public class DefaultAccountManager implements AccountManager{
	 private static final Log LOGGER=
		 LogFactory.getLog(AccountManager.class);
	 public Account findAccountForUser(String userId){
		 LOGGER.debug("Getting account for user ["+userId+"]");
		 ResourceBundle bundle=
			   PropertyResourceBundle.getBundle("technical");
		 String sql=bundle.getString("FIND_ACCOUNT_FOR_USER");
//		 Some code logic to load a user account using JDBC
		 [...]
	          } 
              }
   用LogFactory取得一个Log对象。用PropertyResourceBundle获得了SQL命令。
     你觉得这个代码看起来很好么?我们可以透过这个代码看见两个问题!它们都是同代码的适应性和承受力相关的。第一个问题是,你无法使用一个不同
的Log对象,因为这是由类内部创建的。一般来说,像这样的类应该能使用给定的任何Log,但是不行,做不到!第二个问题,就是PropertyResourceBundl
e。它看上去不错,但是,就是它决定了实现的方式,假使我们想用XML存储配置,就被这个类束缚了!再一次的,不应该由这个类决定使用何种实现,一个有
效的设计策略是,对象不应限制在其现有商业逻辑之外的可传入的对象。外围对象的选择应该能由在更高调用链上的某个人控制。随着调用层的上升,决定使
用哪个特定的记录器或是配置可以在最高层决定。这种策略最大化的提供了代码的灵活性。我们知道唯一不变的就是变化!
         重构所有的代码以传递领域对象是件费时的事。也许你不准备重构整个程序,而仅仅是为了一个单元测试,幸运的是这里有方便的重构技术,让你的代码保持相同的接口,并传递给它没有创建的域对象。让我们看看重构后的DefaultAccountManager类是什么样的! 
[...]
 import java.util.PropertyResourceBundle;
 import java.util.ResourceBundle;;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 [...]
  public class DefaultAccountManager implements AccountManager{
	private Log logger;
	private Configuration configuration;
	public DefaultAccountManager()
	{   this.LogFactory.getLog(DefaultAccountManager.class),
		new DefaultConfiguration("technical"));
    }
	public DefaultAccountManager(Log logger,
			 Configuration configuration)
	{ this.logger=logger;
	  this.configuration=configuration;
    }
	public Account findAccountForUser(String userId){
		this.logger.debug("Getting account for user["+userId+"]");
		this.configuration.getSQL("FIND_ACCOUNT_FOR_USER");
		
	}
}
   注意到,它使用了一个全新的Configuration接口,替换了前一个代码上的PropertyResourceBundle类。由于引入了一个接口(这很容易mock),同
时Configuration接口可以实现你想要的任何东西,这就使得代码变得灵活!你可以用Log和Configuration接口实现任何的实现复用DefaultAccountMan
ager类。这个设计变得更加的优秀了。类可以由外围(它的调用者)控制,同时你并没有破坏已有的接口,因为你只是添加了一个新的构造函数,同时还保留
了原有的构造函数,它仍然对logger和configuration进行初始化,赋给成员默认值。
   重构提供了在测试中控制领域对象的一个暗门。在保留向后兼容性的同时,也重构了一条通向未来的路!
     对于引入暗门使你的代码更加的容易测试,这让很多人担心,那么看看大名鼎鼎的极限编程专家是怎么说的:
    我的车里有一个诊断器和一个测油汁。在炉子的上面和两个侧面都有一个检查孔。我的笔筒是透明的,所以没有墨水,我就可以看见。
    如果我发现在类中有必要添加以个方法,使我能测试它,我也会去做。这经常发生,比如有着简单的接口和复杂的内部函数类可能需要一个
    Extract Class重构。我只是把我理解的类的需求给予类,同时关注它下一步需要什么。
    

             允许灵活的代码

          我们在前面描述了一个设计模式称为控制反转(IOC)的著名模式。主要的想法是从类/方法之外实例化所有的

领域对象,同时传递各种参数。对象实例是传递给类的(通常通过接口),而不是由类来创建对象实例。在上面的例子中,这表现在把Log和Configuration对像传递给DefaultAccountManager类。DefaultAccountManager没有表明传递了哪些实例以及他们是如何构造的。只知道Log和Configuration的接口。

        IOC使单元测试变得简单,为了证明这点,让我们看看你可以多么简单的进行findAccountByUser测试!

  public void testFindAccountByUser(){
	 MockLog logger=new MockLog();
	 MockConfiguration Configuration=new  MockConfiguration();
          Configuration.setSQL("SELECT*[...]");
           DefaultAccountManager am=
    	  new DefaultAccountManager(logger,configuration);
          Account account=am.findAccountForUser("1234");
           //          Perform asserts here 
          [...]
        }
          你已经完全可以在外围控制你的记录和配置行为了!这样你的代码就更加的灵活了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值