利用单体测试学习MarshalByRefObject
 
  今天看了一篇关于关于.Net Remoting的文章,就有点想了解一下的冲动。经过简单的了解,决定先从MarshalByRefObject开始。如何开始呢?MSDN,我想这是一个大家常用的方式,但我还想借用一个更有效手段——单体测试。本文就想和大家一起体验下如何“利用单体测试学习MarshalByRefObject”。
  .Net中不同应用程序域中的对象的通信方式有两种:一种是跨应用程序域边界传输对象副本,一种是使用代理交换消息。而MarshalByRefObject类允许在支持远程处理的应用程序中跨应用程序域边界访问对象。当远程应用程序引用根据值封送的对象时,将跨应用程序域边界传递该对象的副本。当跨应用程序域边界使用类型时,类型必须是从MarshalByRefObject 继承的。因此添加了一个从MarshalByRefObject派生的MarshalObject
public class MarshalObject : MarshalByRefObject
{
    public MarshalObject(int id)
    {
        this.Id = id;
    }
    public override object InitializeLifetimeService()
    {
        ILease lease = (ILease)base.InitializeLifetimeService();
        // By default InitialLeaseTime is set to 5 minutes
        // we set it at 10 seconds
        if (lease.CurrentState == LeaseState.Initial)
        {
            lease.InitialLeaseTime = TimeSpan.FromSeconds(10);
        }
        return lease;
    }
    public int Id {private set; get;}
}
其中,属性Id用于在测试中识别对象。而重写InitializeLifetimeService()则是为了测试用于控制对象生命周期的服务是否与改写后的一致。
 
第一个测试用例:
[TestMethod]
public void CreateObjRef()
{
    MarshalObject objMarshal = new MarshalObject(88);
    RemotingServices.SetObjectUriForMarshal(objMarshal, "MarshalByRefObjectTest.objMarshal1");
    RemotingServices.Marshal(objMarshal);
    ObjRef objRef = objMarshal.CreateObjRef(typeof(MarshalObject));
    Assert.AreEqual(objRef.URI, RemotingServices.GetObjectUri(objMarshal));      
    RemotingServices.Disconnect(objMarshal);
}
其中,先为对象设定指定的URI,然后通过比较创建的对象和获取到的对象的URI是否一致来判断跨应用程序域边界传递的对象副本是否创建成功。这样,我们就掌握了跨应用程序域边界传递的对象副本的创建方法。
 
第二个测试用例:
[TestMethod]
[ExpectedException(typeof(RemotingException))]
public void CreateObjRefThrowException()
{
    MarshalObject objMarshal = new MarshalObject(88);
    ObjRef objRef = objMarshal.CreateObjRef(typeof(MarshalObject));
}
其中CreateObjRef用于创建一个对象,该对象应该包含生成用于与远程对象进行通信的代理所需的全部相关信息。该实例不是有效的远程处理对象就会抛出RemotingException的异常。了解了这一点,是不是就可以使用MarshalByRefObject 类时避免为类似问题大伤脑筋呢?
 
第三个测试用例:
[TestMethod]
public void LifetimeService()
{
    MarshalObject objMarshal = new MarshalObject(88);
    RemotingServices.SetObjectUriForMarshal(objMarshal, "MarshalByRefObjectTest.objMarshal2");
    RemotingServices.Marshal(objMarshal);
    objMarshal.InitializeLifetimeService();
    ILease lease = (ILease)objMarshal.GetLifetimeService();
    Assert.AreEqual(lease.InitialLeaseTime, TimeSpan.FromSeconds(10));
    RemotingServices.Disconnect(objMarshal);
}
该用例测试了MarshalByRefObject实例中用于控制对象生命周期的服务是否与改写后的一致。通过这个用例,我们就可以清晰地明白如何在创建MarshalByRefObject实例的基础上去更改对象生命周期。
 
第四个测试用例:
[TestMethod]
public void GetObject()
{
    MarshalObject objMarshal = new MarshalObject(88);
    RemotingServices.SetObjectUriForMarshal(objMarshal, "MarshalByRefObjectTest.objMarshal3");
    RemotingServices.Marshal(objMarshal);
    TcpChannel chn = new TcpChannel(1294);
    ChannelServices.RegisterChannel(chn);
    object objRem = Activator.GetObject(typeof(MarshalObject), "tcp://localhost:1294/MarshalByRefObjectTest.objMarshal3");
    MarshalObject objMarshalRem = (MarshalObject)objRem;
    Assert.AreEqual(88, objMarshalRem.Id);
    RemotingServices.Disconnect(objMarshal);
    chn.StopListening(null);
    ChannelServices.UnregisterChannel(chn);
}
该用例测试了是否可以通过Tcp通道来传递和获取MarshalByRefObject实例。通过这个实例我们就可以掌握远程传递的基本方法。
 
小结:
  极限编程中一向将单体测试看作一个可执行的文档。通过这样的一个实例大家是否可以感受到这一点呢?也许本文中很多的文字内容都参照了MSDN,但因为有了一个相关的单体测试程序是不是可以让大家可以更容易去了解更多呢?为了有一个更深的体会,我还是强烈建议大家可以利用NUnit或者Visual Studio工具运行下,感觉下什么是“可执行的文档”。通过分析这样一个单体测试代码,我学习了单体测试,同时又了解了MarshalByRefObject,一举两得。不知道大家是不是也想尝试一下呢?
 
参照: