Mock 和 Stub 的异同
Mock 和 Stub 都属于单元测试的范畴,他们有共同点,同时也有不同点。
那就拿我的一个例子去说明,先上一段代码:
public abstract class HttpContextBase
{
protected HttpRequestBase request = null;
protected HttpResponseBase response = null;
public virtual HttpRequestBase Request
{
get
{
return request;
}
set
{
request = value;
}
}
public virtual HttpResponseBase Response
{
get
{
return response;
}
set
{
response = value;
}
}
}
public abstract class HttpRequestBase
{
protected NameValueCollection urlParams = null;
public virtual NameValueCollection Params
{
get
{
return urlParams;
}
set
{
urlParams = value;
}
}
}
public abstract class HttpResponseBase
{
protected string responseMsg = null;
protected string contentType = null;
public virtual string ResponseMsg
{
get
{
return responseMsg;
}
}
public virtual string ContentType
{
get
{
return contentType;
}
set
{
contentType = value;
}
}
public virtual void Write(string message)
{
responseMsg = message;
}
}
本测试主要测试 GetExamHandler 中的ProcessRequest 方法,在其中调用了IExamService的接口方法 GetExams(),然后将结果以application/json的格式返回给客户端。
由于HttpHandler中对于HttpContext不好模拟,因此我自己又封装了一层,并使他们都继承了基类如:HttpContextBase,HttpRequestBase和HttpResponseBase。
[TestMethod]
public void ProcessRequestTest_Mock()
{
string strExpected = "Tested String";
GetExamHandler target = new GetExamHandler();
var mockContext = new Mock<Models.HttpContextBase>();
var mockRequest = new Mock<Models.HttpRequestBase>();
var mockResponse = new Mock<Models.HttpResponseBase>();
var objContext = mockContext.Object;
var objRequest = mockRequest.Object;
var objResponse = mockResponse.Object;
mockContext.Setup(o => o.Request).Returns(objRequest);
mockContext.Setup(o => o.Response).Returns(objResponse);
NameValueCollection urlParams=new NameValueCollection();
urlParams.Add("n", "true");
urlParams.Add("secKey", "IsuibianxieDT");
mockRequest.Setup(o => o.Params).Returns(urlParams);
mockResponse.Setup(o => o.ContentType).Returns("application/json");
var mockExamService=new Mock<IExamService>();
mockExamService.Setup(p => p.GetExams()).Returns(strExpected);
string strActual = string.Empty;
mockResponse.Setup(p => p.Write(strExpected)).Callback<string>(str => strActual = str);
target.ProcessRequest(objContext);
Assert.AreEqual(strExpected, strActual);
}
上面这段代码,利用Mock的方式来模拟HttpContextBase。
public class FakeHttpContext : Models.HttpContextBase { } public class FakeRequest : Models.HttpRequestBase { } public class FakeResponse : Models.HttpResponseBase { } [TestMethod] public void ProcessRequestTest_Stub() {
string strExpected = "Tested String"; GetExamHandler target = new GetExamHandler(); var objContext = new FakeHttpContext(); var objRequest = new FakeRequest(); var objResponse = new FakeRequest(); objContext.Request=objRequest; objContext.Response=objResponse; NameValueCollection urlParams=new NameValueCollection(); urlParams.Add("n", "true"); urlParams.Add("secKey", "IsuibianxieDT"); objRequest.Params=urlParams; var mockExamService=new Mock<IExamService>(); mockExamService.Setup(p => p.GetExams()).Returns(strExpected); string strActual = string.Empty; target.ProcessRequest(objContext); Assert.AreEqual(strExpected, objResponse.ResponseMsg);
}
上面的代码,利用Stub的方式来模拟HttpContextBase,可以看出创建了几个Fake类:FakeHttpContext,FakeRequest和FakeResponse。
根据以上代码,我们总结下二者的相同点:
1.在被测试方法中,都存在对象依赖关系。
2.都是利用多态的方式,通过Test Double 将真正的实现逻辑隔离出来。
3.二者都可以用来进行方法的单元测试。
再来说说二者的不同点:
1.虽然二者都可以进行单元测试,但是对于Stub方法,需要在测试时实现一些Fake的对象逻辑;而Mock不需要实现逻辑。因为Mock框架(如Moq)都已经帮你实现了。
2.关注点不同。Stub方法更加关注对象的状态,比如例子中objResponse.ResponseMsg;而Mock则关注对象的行为,即某个方法是否被执行,如代码中
mockResponse.Setup(p => p.Write(strExpected)).Callback<string>(str => strActual = str); 只有被测试代码中Response.Write(string)被执行时,后面的回调才会起作用。
因此MartinFowler在他的文章《Mocks Aren't Stubs》中说,Stub是State-Based Testing,而Mock是Interaction-Based Testing。因此二者的区别,引用MartinFowler原话就是:The difference is in how exactly the double runs and verifies.