RhinoMock入门——Do,With和Record-playback
(一)Do(delegate)
有时候在测试过程中只返回一个静态的值是不够的,在这种情况下,Do()方法可以用来在方法调用时添加自定义的行为。一般来说,Do()方法会替换方法调用。它的返回值会从模拟的调用中返回(即使是有异常发生也是这样)。Do()的参数委托委托的方法的签名须和方法的签名匹配。只有当签名匹配时才能生效,且一个匹配生效一次。
看官方给出的例子:
public class Speaker
{
private readonly string firstName;
private readonly string surname;
private INameSource nameSource;
public Speaker(string firstName, string surname, INameSource nameSource)
{
this.firstName = firstName;
this.surname = surname;
this.nameSource = nameSource;
}
public string Introduce()
{
string name = nameSource.CreateName(firstName, surname);
return string.Format("Hi, my name is {0}", name);
}
}
{
private readonly string firstName;
private readonly string surname;
private INameSource nameSource;
public Speaker(string firstName, string surname, INameSource nameSource)
{
this.firstName = firstName;
this.surname = surname;
this.nameSource = nameSource;
}
public string Introduce()
{
string name = nameSource.CreateName(firstName, surname);
return string.Format("Hi, my name is {0}", name);
}
}
public interface INameSource
{
string CreateName(string firstName, string surname);
}
{
string CreateName(string firstName, string surname);
}
现在演讲者和名字分开在两个类中。然后进行自我介绍,介绍时要介绍自己的姓名,即FirstName+LastName。在介绍中要用到InameSource中的CreateName方法,接下来将会模拟这个接口,而通过其它的方法来替代。
[Test]
public void SayHelloWorld()
{
MockRepository mocks = new MockRepository();
INameSource nameSource = mocks.DynamicMock<INameSource>();
Expect.Call(nameSource.CreateName(null, null))
.IgnoreArguments()
.Do(new NameSourceDelegate(Formal));
mocks.ReplayAll();
string expected = "Hi, my name is Ayende Rahien";
string actual = new Speaker("Ayende", "Rahien", nameSource).Introduce();
Assert.AreEqual(expected, actual);
}
delegate string NameSourceDelegate(string first, string surname);
private string Formal(string first, string surname)
{
return first + " " + surname;
}
public void SayHelloWorld()
{
MockRepository mocks = new MockRepository();
INameSource nameSource = mocks.DynamicMock<INameSource>();
Expect.Call(nameSource.CreateName(null, null))
.IgnoreArguments()
.Do(new NameSourceDelegate(Formal));
mocks.ReplayAll();
string expected = "Hi, my name is Ayende Rahien";
string actual = new Speaker("Ayende", "Rahien", nameSource).Introduce();
Assert.AreEqual(expected, actual);
}
delegate string NameSourceDelegate(string first, string surname);
private string Formal(string first, string surname)
{
return first + " " + surname;
}
看上段测试的粗体部分。
Do参数是委托类型,其中这个委托类型委托的方法的签名要和模拟对象中期望的要替换的方法的签名一致,即:
private string Formal(string first, string surname)
string CreateName(string firstName, string surname);
string CreateName(string firstName, string surname);
两者相匹配。
然后当对演讲者构造时:
new Speaker("Ayende", "Rahien", nameSource)
会对演讲者三个域进行赋值
private readonly string firstName;
private readonly string surname;
private INameSource nameSource;
private readonly string surname;
private INameSource nameSource;
接下来进行介绍时,调用方法:
public string Introduce()
{
string name = nameSource.CreateName(firstName, surname);
return string.Format("Hi, my name is {0}", name);
}
{
string name = nameSource.CreateName(firstName, surname);
return string.Format("Hi, my name is {0}", name);
}
而这个方法则由Do方法的委托参数委托的方法来替代:
private string Formal(string first, string surname)
{
return first + " " + surname;
}
{
return first + " " + surname;
}
返回FirstName+空格+LastName
(二)With
流畅式的期望和验证语法。什么是流畅式?先看例子:
[Test]
public void TestFluent()
{
MockRepository mocks = new MockRepository();
var customer = mocks.DynamicMock<ICustomer>();
string strTemp=string.Empty;
With.Mocks(mocks)
.Expecting(
delegate
{
Expect.Call(customer.ShowTitle("")).Return("with 语句");
})
.Verify(
delegate
{
strTemp = customer.ShowTitle("");
});
Assert.AreEqual("with 语句", strTemp);
}
public void TestFluent()
{
MockRepository mocks = new MockRepository();
var customer = mocks.DynamicMock<ICustomer>();
string strTemp=string.Empty;
With.Mocks(mocks)
.Expecting(
delegate
{
Expect.Call(customer.ShowTitle("")).Return("with 语句");
})
.Verify(
delegate
{
strTemp = customer.ShowTitle("");
});
Assert.AreEqual("with 语句", strTemp);
}
这就是所谓的流畅式。通过匿名委托来实现。如果在匿名委托中完成则会隐匿调用ReplayAll()和mocks.VerifyAll()。
如果要启用次序,则可使用:ExpectingInSameOrder,例如:
[Test]
public void TestFluent()
{
MockRepository mocks = new MockRepository();
var customer = mocks.DynamicMock<ICustomer>();
string strTemp=string.Empty;
With.Mocks(mocks).ExpectingInSameOrder(
delegate
{
Expect.Call(customer.ShowTitle("")).Return("with 语句");
Expect.Call(customer.Unid).Return(1);
})
.Verify(
delegate
{
strTemp = customer.ShowTitle("");
int i = customer.Unid;
});
Assert.AreEqual("with 语句", strTemp);
}
public void TestFluent()
{
MockRepository mocks = new MockRepository();
var customer = mocks.DynamicMock<ICustomer>();
string strTemp=string.Empty;
With.Mocks(mocks).ExpectingInSameOrder(
delegate
{
Expect.Call(customer.ShowTitle("")).Return("with 语句");
Expect.Call(customer.Unid).Return(1);
})
.Verify(
delegate
{
strTemp = customer.ShowTitle("");
int i = customer.Unid;
});
Assert.AreEqual("with 语句", strTemp);
}
With语句的隐式使用
With可以隐式的创建Mock实例,并自动调用VerifyAll方法。
[Test]
public void TestWithMocker()
{
With.Mocks(
delegate
{
var customer = Mocker.Current.DynamicMock<ICustomer>();
Expect.Call(customer.ShowTitle("")).Return("with 语句");
Mocker.Current.ReplayAll();
Assert.AreEqual("with 语句", customer.ShowTitle(""));
});
}
public void TestWithMocker()
{
With.Mocks(
delegate
{
var customer = Mocker.Current.DynamicMock<ICustomer>();
Expect.Call(customer.ShowTitle("")).Return("with 语句");
Mocker.Current.ReplayAll();
Assert.AreEqual("with 语句", customer.ShowTitle(""));
});
}
这里才看出With确实很流畅。
下边说一下由显式创建Mock实例来代替隐式创建:
[Test]
public void TestWithMockerr()
{
MockRepository mocks = new MockRepository();
With.Mocks(mocks,
delegate
{
var customer = Mocker.Current.DynamicMock<ICustomer>();
Expect.Call(customer.ShowTitle("")).Return("with 语句");
Mocker.Current.ReplayAll();
Assert.AreEqual("with 语句", customer.ShowTitle(""));
});
}
public void TestWithMockerr()
{
MockRepository mocks = new MockRepository();
With.Mocks(mocks,
delegate
{
var customer = Mocker.Current.DynamicMock<ICustomer>();
Expect.Call(customer.ShowTitle("")).Return("with 语句");
Mocker.Current.ReplayAll();
Assert.AreEqual("with 语句", customer.ShowTitle(""));
});
}
没多大区别。在使用Mocker.Current时,不能在嵌套中使用,因为这是个全局的,而With.Mocks会重写Mocker.Current
(三)Record-PlayBack
Rhinomock支持一种通过Using语句来进行录制回放的方式。
[Test]
public void TestRecordPlayback()
{
MockRepository mocks = new MockRepository();
var customer = mocks.DynamicMock<ICustomer>();
using (mocks.Record())
{
Expect.Call(customer.ShowTitle("")).Return("录制回放");
}
using (mocks.Playback())
{
Assert.AreEqual("录制回放", customer.ShowTitle(""));
}
}
public void TestRecordPlayback()
{
MockRepository mocks = new MockRepository();
var customer = mocks.DynamicMock<ICustomer>();
using (mocks.Record())
{
Expect.Call(customer.ShowTitle("")).Return("录制回放");
}
using (mocks.Playback())
{
Assert.AreEqual("录制回放", customer.ShowTitle(""));
}
}