JGTM'2008 [MVP]

Thinking in WPF...
随笔 - 36, 评论 - 453, 引用 - 16

导航

标签

每月存档

最新留言

广告

 

前面用透明代理机制实现简单AOP的文章一发,很快收到很多朋友的反馈,其中就有一些朋友提出关于文中所述的透明代理等技术还能够用于什么应用场合的疑问。说实在的,本来我就是想写一些关于这些技术在实际项目中应用的随笔的,可是想到可能会有很多朋友对这些技术的基本原理还不熟悉,这才萌生了先写一系列文章作铺垫的念头(可惜由于工作比较忙的缘故,至今还欠一篇没有写完,还请大家见谅啦,我抓紧时间补上——这篇内容更精彩噢!呵呵)。

 

今天我们又一次把我前面这两篇文字中提到的技术应用在项目开发中,初步反馈还是非常positive的,这里我不妨把问题先说出来,请看过文章的朋友们好好想想,看看如何利用已经学到的知识来解决这个实际问题吧——稍后我会回来说说我的想法(当然如果大家看过偶的文章以后都能够想出这种解决方案的话那当然就最好了:),当然如果你有其他更好的办法,更是欢迎你通过发表评论和大家分享!

 

这个问题和单元测试(Unit Testing)有密切关系(虽然我们的项目还不能算是完全的TDD,但是充分、必要的单元测试还是不可或缺),这里我们还是以简单的例子来说明,比如这里有一个银行服务的接口:

 

interface IBankService
{
  void Withdraw(Account account, int amount);
  void Deposit(Account account, int amount);
  int QueryBalance(Account account);
}

 

这个接口的三个方法的作用很简单:取款、存款、查询余额。我们肯定要开发一个实现这个接口的具体业务操作类;与此同时,由于该服务接口对安全性的严格要求,项目组中专门负责安全规则的部门将以DECORATOR的方式针对该接口的每一个方法编写负责安全保障的修饰类(最终在运行时由组件工厂动态组合)。以这种设计方式,只要确定了服务接口,则两个类的开发就可以充分的并行进行了——真的是这样吗?我们写写看:

 

class BankServiceSecurityGuard: ComponentDecorator, IBankService
{
  void IBankService.Withdraw(Account account, int amount)
  {
    if ( … )
      throw new DailyAmountExceedsException( … );
    if ( … )
      throw new AccountBlockedException( … );
    if ( … )
      throw new PermissionNotGrantedException( … );
   

    ((IBankService)Decoratee).Withdraw(account, amount);   
  }
 
}

 

这个方法的实现就是一个把门儿的逻辑:结合方法调用参数使尽各种必要手段确保只有在符合所有安全规则的前提下才能够把调用转发到被修饰的(即decoratee)对象(也就是IBankService的核心实现类上)。也就是说,开发并测试这个安全保障修饰类的时候最终还是要依赖于一个能够工作的核心实现类,否则就算可以编译通过(还好这种依赖已经被接口隔离开了),你也无法让单元测试通过。我们能不能用什么办法来独立的测试这个修饰对象使之不依赖于一个正确工作的核心实现类呢?换句话说,有没有办法在没有可工作的核心实现类的情况下(这种情况常见于核心类开发周期较长的时候),让下面针对安全保障修饰类的单元测试代码正常起到单元测试的作用(一是驱动开发过程、二是形成软件规格说明、三是跟踪软件缺陷……)呢(我们先假设Account类是已经存在且经过充分测试的——其实对该类的隔离测试也是类似的道理)?

 

[TestFixture]
public class BankServiceSecurityTest
{
  private IBankService bankService;

  [SetUp]
  public void Initialization()
  {
    bankService = new BankServiceSecurityGuard(???);
  }

  [ExpectedException(typeof(DailyAmountExceedsException))]
  public void TestForExceedingDailyAmount()
  {
    AccountState state = AccountState.DailyAmountReached;
    Account account = new MockAccount(state);
    bankService.Withdraw(10);
  }

  public void TestForNormalWithdraw()
  {
    AccountState state = AccountState.Normal;
    int initialBalance = 1000;
    Account account = new MockAccount(state, initialBalance);
    try
    {
      bankService.Withdraw(10);
      Assert.IsTrue(coreBankService's been called for Withdraw(10));
    }
    catch(Exception ex)
    {
      Assert.Fail("this call should not throw exception");
    }   
  }
}

 

简单说,我们要测试两类情况:一类是应该抛出安全异常的情况(TestForExceedingDailyAmount),在这种情况下,我们需要确信在给定的前提下必将产生指定的后果(比如特定类型的安全异常);另一类则是不该抛出异常的情况喽,在这种情况下我们不光要确信没有安全异常抛出,还要进一步确保方法调用确实被正确的(包括传入参数的值)转发给内部的指定方法了(否则一是有可能放过人为的安全后门,也有可能漏过修饰类忘记或错误调用被修饰类的情况)!

 

我已经听见有人高呼“简单简单”啦……写个mock对象不就结了!好,现在请你来为系统中数以百计的安全修饰类以及其他功能修饰类编写一一对应的mock对象好啦(幸好我们的接口都不是很fat,每个里面平均也就不到10个方法吧:)!反正我可真是一个超懒的程序员啊……你能给个省事儿又简单的方案吗?

打印 | 张贴于 2004-03-02 21:09:00 | Tag:.NET Stuffs  Agile/XP

留言反馈

#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
按照msdn上的方法,稍微修改一下应改可以满足要球
2004-03-05 12:25:00 | [匿名:kain]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
@kain:

也许是因为我的文章写得比较匆忙,确实有很多细节都没有深入阐释,如果因此而造成大家理解上的困难,还希望与我多多交流,把疑惑理清。

学习方式确实是一个很重要的问题——方式对了,四两拨千斤;方式不对,事倍而功半。所以我一直把握这样一个原则,就是“从问题出发,从实际出发”,授人以渔而非授人以鱼,希望初学者能够多多体会,找到适合自己的有效的学习方式,从而真正体会到学习和成长的乐趣,咳咳…… ;-)
2004-03-03 13:23:00 | [匿名:JGTM'2004 [MVP]]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
感谢JGTM的批评指正

作为初学者我确实有点心浮气躁。

刚开始看到文章的时候我也确实想过,但是毕竟自身.net并不是那么熟悉

虽然自己知道AOP的一些基本的概念,对于横切在.net中的实现我确实不知道(因为我对于你上篇的文章提到的一些对象都没有透彻的理解,看msdn的介绍也是比较晦涩)。

可能使自己的学习方式不正确吧,再努力...
2004-03-03 13:05:00 | [匿名:kain]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
@mvm:

偶本来是想把两种类型的mock的实现都提出来(一种是无状态的服务类;一种是有状态的实体类),不过因为原理上差不多,而且无状态的mock更简单,所以这里先举了这一个例子——还用不到reflection/attributes(不过实现有状态的mock时这两个技术就大可有所作为了)呢,而且代码只有20多行,一个类而已。:)

在这个基础上我们稍后再讨论对有状态对象实现adapable mock objects的机制。
2004-03-03 12:37:00 | [匿名:JGTM'2004 [MVP]]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
@kain:

文章大家都读过,可是遇到问题你有没有自己的思路呢?怎么样把从文章中学到的东西真正应用起来呢?如果达不到这个效果,看再多的文章又有何用呢?所以我鼓励大家,就算不知道怎么解决问题,至少也要动脑筋想想,把一个不能解决的问题分解成一个个更小的问题,直到所有的问题都是可以解决的,再回溯回去,原来不能解决的问题不就能解决了吗?:)
2004-03-03 12:32:00 | [匿名:JGTM'2004 [MVP]]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
在Java也不容易,就算用jMock作,仍然要为每个期待方法设定假的返回值。如果方法多......
2004-03-03 09:17:00 | [匿名:冰云]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
.net里面也还可以,用Attribute和reflection

比较起来,还是COM的AOP实现起来最麻烦

不过总的说来,都很麻烦。都不太自然。

AOP感觉和decoration很像。
2004-03-03 08:46:00 | [匿名:mvm]
#回复: Unit Testing with .NET Quiz: Adaptable Mock Objects? 编辑
在程序员今年的第一期讲到了这个问题,如果是在Java中来实现比较容易。在.net中还不知道,继续关注...
2004-03-03 08:41:00 | [匿名:kain]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode.MVC引擎 0.5.2.0