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();
            var mockRequest = new Mock();
            var mockResponse = new Mock();
            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();
            mockExamService.Setup(p => p.GetExams()).Returns(strExpected);
            string strActual = string.Empty;
            mockResponse.Setup(p => p.Write(strExpected)).Callback(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();
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(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.