2012-02-14 106 views
3

我有一点完美的风暴阻止了我测试课程。该课程是RestClient,它包装了内部的HttpClient(我不能修改)。 HttpClient上的ExecuteMethod方法无效。它接受IHttpMethod,并根据来自服务器的响应修改此对象。我想嘲笑ExecuteMethod,以便它为我修改IHttpMethod。我正在尝试使用Callback来实现此目的,但它不起作用。使用Moq将参数修改为虚拟无效方法

这里的发送请求的代码:

var httpClient = this.httpClientFactory.CreateHttpClient(); 
httpClient.ExecuteMethod(method); 

var response = this.GetResourceResponse<T>(method.ResponseBodyAsStream.AsString()); 
response.ResponseHeaders = method.ResponseHeaders; 
response.Status = method.StatusCode.ToString(); 
response.StatusCode = (int)method.StatusCode; 

return response; 

这是我的企图在嘲讽:

var mockHttpMethod = new Mock<IHttpMethod>(); 
mockHttpMethod.Setup(m => m.ResponseBodyAsStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes("foo"))); 

var modifyHttpMethod = new Action<IHttpMethod>(m => 
{ 
    m = mockHttpMethod.Object; 
}); 

var mockHttpClient = new Mock<IHttpClient>(); 
mockHttpClient.Setup(c => c.ExecuteMethod(It.IsAny<IHttpMethod>())) 
    .Callback<IHttpMethod>(modifyHttpMethod); 

var mockHttpClientFactory = new Mock<ILegalHoldHttpClientFactory>(); 
mockHttpClientFactory.Setup(f => f.CreateHttpClient()).Returns(mockHttpClient.Object); 

var restClient = new RestClient(mockHttpClientFactory.Object); 

当执行modifyHttpMethod行动,我看到两件事情,这两者我期待:

  1. 传入IHttpMethodm)有p我期望它具有的性能。
  2. 将模拟对象分配给m后,它包含我在测试中设置的残段值。

然而,在执行后回调,并控制返回到我的应用程序的代码,我method变量仍然有我在上面的步骤1,尝试读取method.ResponseBodyAsStream时导致空引用异常看到了它的旧值。

我试图做什么甚至可以实现?如果是这样,怎么样?谢谢!

回答

2

我复制你的设置相一相嘲讽,并不能找到它的任何问题:

public interface IHttpMethod 
{ 
    MemoryStream ResponseBodyAsStream { get; set; } 
} 

public interface IHttpClient 
{ 
    void ExecuteMethod(IHttpMethod method); 
} 

public class HttpClient : IHttpClient 
{ 

    #region IHttpClient Members 

    public void ExecuteMethod(IHttpMethod method) 
    { 

    } 

    #endregion 
} 

public class Factory 
{ 
    public virtual IHttpClient CreateHttpClient() 
    { 
     return new HttpClient(); 
    } 
} 

public class ClassUnderTest 
{ 
    private readonly Factory _factory; 

    public ClassUnderTest(Factory factory) 
    { 
     _factory = factory; 
    } 

    public string GetResponseAsString(IHttpMethod method) 
    { 
     var myClient = _factory.CreateHttpClient(); 
     myClient.ExecuteMethod(method); 

     return method.ResponseBodyAsStream.ToString(); 
    } 
} 

[TestClass] 
public class ScratchPadTest 
{ 
    [TestMethod] 
    public void SampleTest() 
    { 
     var mockHttpMethod = new Mock<IHttpMethod>(); 
     mockHttpMethod.Setup(x => x.ResponseBodyAsStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes("foo"))); 

     var modifyHttpMethod = new Action<IHttpMethod>(m => 
     { 
      m = mockHttpMethod.Object; 
     }); 

     var mockHttpClient = new Mock<IHttpClient>(); 
     mockHttpClient.Setup(c => c.ExecuteMethod(It.IsAny<IHttpMethod>())).Callback<IHttpMethod>(modifyHttpMethod); 

     var myFactoryStub = new Mock<Factory>(); 
     myFactoryStub.Setup(f => f.CreateHttpClient()).Returns(mockHttpClient.Object); 

     var myCut = new ClassUnderTest(myFactoryStub.Object); 

     Assert.IsNotNull(myCut.GetResponseAsString(mockHttpMethod.Object)); 
    } 

} 

该测试通过,这意味着内存流不为空(否则一个例外是生成)。我可以看到的唯一X因素是你的AsString()扩展方法(我假设这是一个扩展方法,因为intellisense并没有在MemoryStream中显示给我)。你的问题能在那里吗?

而且,顺便说一句,你试图做的事情几乎可以肯定是用Moq实现的。

+0

我不认为问题是与扩展方法。我可以看到扩展方法如何读取流并关闭它,使其不可用,但在AsString()方法执行之前流实际上不可用。我不知道为什么你的代码可以工作,而我不知道。我最终做了一些重构,使其更具可测性,所以这不再是一个问题,但我仍然希望它能够工作,因为它看起来像一种有用的技术。非常感谢您的回复。 – Samo 2012-02-15 17:27:33

+0

没问题 - 如果你有一个更可测试的方法,那总是很好。 :)但是,我会拿走的是,我敢肯定,你可以以某种方式或另一种方式完成Moq想要的东西。有时到达那里并不容易,但它非常灵活。 – 2012-02-15 17:34:35