解耦配置文件依赖,让单元测试跑起来

 现在很多人都开始使用ioc框架了。新公司里也项目已在使用unity来管理对象依赖。将对象之间的依赖从程序里面放到配置文件里面,确实是我们的系统更加的灵活了
我们能很容易的通过替换对象来实现程序的功能切换。另外的一个好处我觉得很多人都不太重视。那就是对象间的解耦给自动化的单元测试提供了可能性
想象下我们测试我们的业务逻辑代码。如果业务逻辑代码使用了数据库,网络。比如处理一个订单这个用例。这个用例包括根据用户购买的产品和用户的等级来计算价格
并生成订单插入到数据库中,然后给客户发送邮件通知。如果我们想要测试我们的计算价格部分代码。如果各个模块是耦合在一起的,为了测试计算价格部分的代码,你就得准备好数据库
和邮件服务器。在这种情况下能够实现自动化测试,是不可能的。你不能保证数据库中的数据是你期望的。你也不能保证邮件服务器一直都运行正常。
所以如果我们的对象之间的依赖是松耦合的(通过接口),我们就能替换数据库访问层对象和邮件发送对象为mock对象来模拟数据库和邮件服务器
。这样我们才能保持我们的测试计算订单的单元测试每次都能按照期望的方式运行。我觉得这是使用ioc解耦带来的一个很重要的好处。甚至比能够实现功能替换更实用。
不知道有谁现在体会到了能够实现功能替换带来的好处。将业务层代码和数据访问层代码解耦后,可以切换数据库实现。但是有几个会这么做的
所以说能够实现自动化单元测试实际上比能切换数据库实现看起来更实用些。

    上面的讨论主要是说明解耦的一个很重要的好处,就是方便单元测试。下面写两个使用ioc来管理对象的例子。虽然都是用了容器来管理依赖。但最后一个对单元测试
更友好一点。因为它不过分依赖外部的配置文件。我们很容易就能把在单元测试中测试。

ExpandedBlockStart.gif 代码
   public   class  Order
    {
        
// some properties

        
public   double  Price {  get set ; }
        
public   string  UserEmail {  get set ; }
    }
    
public   interface  IOrderService
    {
        
void  CreateOrder(Order order);
    }
    
public   interface  IEmailSender
    {
        
void  Send( string  email, string  content);

    }
    
public   class  EmailSender:IEmailSender
    {
        
public   void  Send( string  email,  string  content)
        {
            
// access network
        }
    }
    
public   interface  IOrderRepository
    {
        
void  SaveOrder(Order order);
    }
    
public   class  OrderRepository:IOrderRepository
    {
        
public   void  SaveOrder(Order order)
        {
            
// access DB
        }
    }
    
public   class  OrderServiceV1:IOrderService
    {

        
public   void  CreateOrder(Order order)
        {
            IOrderRepository orderRepository 
=  IocContainer.Resolve < IOrderRepository > ();  // 获取OrderRepository实现
            IEmailSender emailSender  =  IocContainer.Resolve < IEmailSender > ();

            order.Price 
=   11 ; // 计算价格算法...

            orderRepository.SaveOrder(order);

            emailSender.Send(order.UserEmail, 
" thanks! " );

        }

    }
    
public   class  OrderServiceV2 : IOrderService
    {
        
// 将依赖接口从方法中分离方便单元测试时替换mock实现
        IOrderRepository orderRepository;
        IEmailSender emailSender;

        
// 默认构造函数从容器中获取实现
         public  OrderServiceV2()
        {
            orderRepository 
=  IocContainer.Resolve < IOrderRepository > ();  // 获取OrderRepository实现
            emailSender  =  IocContainer.Resolve < IEmailSender > ();
        }
        
// 提供不依赖配置文件的接口实现
         public  OrderServiceV2(IOrderRepository orderRepository, IEmailSender emailSender)
        {
            
this .orderRepository  =  orderRepository;
            
this .emailSender  =  emailSender;
        }

        
public   void  CreateOrder(Order order)
        {
            orderRepository 
=  IocContainer.Resolve < IOrderRepository > ();  // 获取OrderRepository实现
            emailSender  =  IocContainer.Resolve < IEmailSender > ();

            order.Price 
=   11 ; // 计算价格算法...

            orderRepository.SaveOrder(order);

            emailSender.Send(order.UserEmail, 
" thanks! " );

        }

    }

当然也可以把OrderServiceV2默认构造函数也去掉。让我们的Service彻底和配置文件和容器解耦。然后在需要IOrderService时也从
容器中获取。并通过构造函数注入对IOrderRepository和IEmailSender的实现。OrderServiceV1则很糟糕。解耦了对具体对象实现的依赖,缺引入了对配置文件的依赖
要想运行单元测试必须先配置好容器的配置文件。还要处理依赖的依赖。比如A依赖B,B依赖C.你必须配置好B的实现,还的配置好C的实现。一直到谁都不依赖为止
其实你运行A是只需要处理好和A的直接依赖对象就行了。根本不用考虑间接依赖。

千万不小忽视这点小的差别。第一种实现过分的依赖配置文件会让写单元测试的人很快就因为麻烦而放弃写自动化的单元测试。最后大家单元测试项目就演变成了调试项目了



总结:不要把任何读配置文件的代码放到实现方法中。应该放到构造函数或者属性。允许单元测试时方便的替换配置文件内容。
像ConfigurationManager.AppSettings["xxx"]这样的语句也不能出现在实现方法中。对容器的使用时最好能采用约定大于配置的方式。减少配置文件大小,减轻配置管理任务
比如IOrderRepository的实现是OrderRepository就不需要配置了。根据命名规则读取实现就行了!


转载于:https://www.cnblogs.com/xhan/archive/2010/06/13/1757868.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值