终于在下班前,把设置NetworkStream.Read 的 buffer 参数的难点搞定了。
2008.01第一次在网上 show 一下自己的代码:
又refact了一下,抽取出 MockHelper class
namespace
aaa.bbb.Test
...
{

/**//// <summary>
/// MockHelper 的摘要说明。
/// Rhino.Mocks 2.9.5 for .Net 1.1
/// </summary>
/// <remarks>
/// 由于 Rhino.Mocks 是在 VerifyAll() 时,才去取变量的值作为 Expected,
/// 而不是在类似 Expect.Call() 时马上取变量的值作为 Expected
/// 因此创建本 Helper 类,利用 Queue 保存 Expect.Call() 时的 Expected Value,
/// 供Rhino.Mocks在 VerifyAll() 时获取
/// </remarks>

public class MockHelper ...{
private Queue expectedStrings;

public delegate bool NetworkStreamWriteDelegate(byte[] actualBuffer, int actualOffset, int actualSize);

public delegate int NetworkStreamReadDelegate(byte[] buffer, int offset, int size);


public MockHelper() ...{
expectedStrings = new Queue(5);
}


public void SetExpectedString(string aString) ...{
expectedStrings.Enqueue(aString);
}


/**//// <summary>
/// 当 Message 中包含有“实际时间”时,根据时间戳标记,分段 Assert
/// </summary>
/// <param name="actualBuffer"></param>
/// <param name="actualOffset"></param>
/// <param name="actualSize"></param>
/// <returns></returns>

public bool AssertWriteMessageWithTimeStamp(byte[] actualBuffer, int actualOffset, int actualSize) ...{
Assert.AreEqual(0, actualOffset);

string expected = (string)expectedStrings.Dequeue();
string pat = @">%d([-:dMyHms ]+)</";
Regex re = new Regex(pat);
MatchCollection mc = re.Matches(expected);
string actualBufferString = Encoding.ASCII.GetString(actualBuffer);
if (0 == mc.Count) System.Console.Out.WriteLine("Non regex matched {0} {1}", pat, expected);
Assert.AreEqual(expected.Substring(0, mc[0].Groups[0].Index - 1), actualBufferString.Substring(0, mc[0].Groups[0].Index - 1));
System.DateTime actualTimeStamp;
Match m;
int startPos;

for (int i = 0; i < mc.Count - 1; i++) ...{
m = mc[i];
//18-Dec-2007 17:57:10 "dd-MMM-yyyy HH:mm:ss"
actualTimeStamp = System.DateTime.ParseExact(actualBufferString.Substring(m.Groups[1].Index - 2 * i, m.Groups[1].Length)
, m.Groups[0].Value
, System.Globalization.DateTimeFormatInfo.InvariantInfo);
// 时间差小于2秒
Assert.Greater(2, System.Math.Abs(System.DateTime.Now.Subtract(actualTimeStamp).TotalSeconds));

Assert.AreEqual(expected.Substring(m.Groups[1].Index + m.Groups[1].Length, mc[i+1].Groups[1].Index - m.Groups[1].Index)
, actualBufferString.Substring(actualBufferString.Length - expected.Length, expected.Length));
}
m = mc[mc.Count - 1];
actualTimeStamp = System.DateTime.ParseExact(actualBufferString.Substring(m.Groups[1].Index - 2 * mc.Count, m.Groups[1].Length)
, m.Groups[1].Value
, System.Globalization.DateTimeFormatInfo.InvariantInfo);
// 时间差小于2秒
Assert.Greater(2, System.Math.Abs(System.DateTime.Now.Subtract(actualTimeStamp).TotalSeconds));

startPos = m.Groups[1].Index + m.Groups[1].Length;
Assert.AreEqual(expected.Substring(startPos)
, actualBufferString.Substring(startPos - 2 * mc.Count));

Assert.AreEqual(expected.Length - 2 * mc.Count, actualSize);
return true;
}


public int GetReadBuffer(byte[] buffer, int offset, int size) ...{
byte[] expectedBuffer = Encoding.ASCII.GetBytes(expectedStrings.Dequeue().ToString());

for (int pos = 0; pos < expectedBuffer.Length; pos++) ...{
buffer[pos] = expectedBuffer[pos];
}
return expectedBuffer.Length;
}

}
}
[TestFixture]

/**/
/// <summary>
/// FirstFixture 的摘要说明。
/// Rhino.Mocks 2.9.6 for .Net1.1
/// </summary>
public
class
FirstFixture
...
{

MockRepository mocks = new MockRepository();
System.IDisposable order;

ITcpClient clientMock;
INetworkStream streamMock;
MockHelper aHelper;
MainClass a;


public FirstFixture() ...{}

[SetUp]

public void SetUp() ...{
order = mocks.Ordered();
clientMock = (ITcpClient)mocks.CreateMock(typeof(ITcpClient));
streamMock = (INetworkStream)mocks.CreateMock(typeof(INetworkStream));
aHelper = new MockHelper();

a = new MainClass(clientMock);
clientMock.Connect(serverIp, serverPort);
Expect.Call(clientMock.GetStream()).Return(streamMock);
Expect.On(clientMock).Call(clientMock.GetLocalPort()).Return(localPort);
}

[TearDown]

public void TearDown() ...{
mocks.VerifyAll();
}

[Test]

public void Normal() ...{
expectedString = new string;

expectedString = "static %yyyy-mm-dd write string";
streamMock.Write(Encoding.ASCII.GetBytes(expectedString), 0, expectedString.Length);

// write的buffer中,包含了当前的时间戳信息。利用Callback实现复杂的assert
streamMock.Write(null, 0, 0);
aHelper.SetExpectedString(expectedRequestBody);
LastCall.Callback(new MockHelper.NetworkStreamWriteDelegate(aHelper.AssertNormalWriteMessage));

// 利用Expect.Do()和delegate,实现对多次Stream.Read()的mock
// 这是在Rhino.Mocks google group中Stream.Read的贴子的基础上完善的。
// 感谢Terriy
byte[] responseBuffer = new byte[1024];
aHelper.SetExpectedString("first server response");
Expect.Call(streamMock.Read(responseBuffer, 1, 1024)).Constraints(Is.TypeOf(typeof(byte [])), Is.Equal(0), Is.Equal(responseBuffer.Length))
.Do(new MockHelper.NetworkStreamReadDelegate(aHelper.SetReadBuffer));

aHelper.SetExpectedString("server second string");
Expect.Call(streamMock.Read(responseBuffer, 2, 1024)).Constraints(Is.TypeOf(typeof(byte [])), Is.Equal(0), Is.Equal(responseBuffer.Length))
.Do(new MockHelper.NetworkStreamReadDelegate(aHelper.SetReadBuffer));

clientMock.Close();

order.Dispose();
mocks.ReplayAll();
a.Interact();
}