【问题标题】:Using Moq to modify the argument to a stubbed void method使用 Moq 将参数修改为 stubbed void 方法
【发布时间】:2012-02-16 00:39:10
【问题描述】:

我遇到了一场完美风暴,让我无法测试课程。该类是一个RestClient,它包装了一个内部HttpClient(我无法修改)。 HttpClient 上的 ExecuteMethod 方法无效。它接受IHttpMethod,并根据来自服务器的响应修改此对象。我想模拟出ExecuteMethod,以便它为我修改IHttpMethod。我正在尝试使用Callback 来实现这一点,但它不起作用。

这是发送请求的代码:

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. 传入的IHttpMethod (m) 具有我期望它具有的属性。
  2. 将模拟对象分配给m 后,它包含我在测试中设置的存根值。

但是,在执行回调并将控制权返回给我的应用程序代码后,我的 method 变量仍然具有我在上面第 1 步中看到的旧值,这会在尝试读取 method.ResponseBodyAsStream 时导致空引用异常.

我正在尝试做的事情甚至可以实现吗?如果是这样,怎么做?谢谢!

【问题讨论】:

    标签: c# moq void


    【解决方案1】:

    我已经通过 vis 模拟复制了您的设置,但找不到任何问题:

    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() 扩展方法(我假设这是一种扩展方法,因为智能感知不会在 MemoryStream 上向我显示它)。你的问题可能在那里吗?

    顺便说一句,你想要做的事情几乎肯定可以通过 Moq 实现。

    【讨论】:

    • 我认为问题不在于扩展方法。我可以看到扩展方法如何读取流并关闭它,使其不可用,但在 AsString() 方法甚至执行之前,流实际上是不可用的。我不确定为什么您的代码有效而我的无效。我最终做了一些重构以使其更具可测试性,所以这不再是一个问题,但我仍然想让它工作,因为它似乎是一种有用的技术。非常感谢您的回复。
    • 没问题——如果你有一个更可测试的方法,那总是好的。 :) 不过,我要带走的是,我很确定您可以通过 Moq 以某种方式完成您想要的事情。有时到达那里并不容易,但它非常灵活。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 2013-12-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多