【问题标题】:How to set HttpRequest.InputStream value using reflection如何使用反射设置 HttpRequest.InputStream 值
【发布时间】:2013-02-06 23:39:45
【问题描述】:

我正在为从 Request.InputStream 读取 POST 正文的 WebApi 控制器编写单元测试。我需要设置 HttpContext.Current.Request.InputStream 的 inputstream 属性,或者设置 inputstream 的内容。到目前为止,这是我的单元测试代码,但它不断抛出异常:

var originalStream = HttpContext.Current.Request.InputStream;
Stream newStream = new MemoryStream(ASCIIEncoding.Default.GetBytes("Test String"));

var propInfo = originalStream.GetType().GetProperty("CanWrite");
propInfo.SetValue(originalStream, true);

newStream.CopyTo(originalStream);

propInfo.SetValue(originalStream, false);

我在 SetValue 行收到以下异常:

ArgumentException: Property set method not found

我做错了吗?我的控制器读取输入流并将其反序列化为 JSON,因此我需要能够将数据插入到该流中。我只是不知道该怎么做。非常感谢。

【问题讨论】:

    标签: c# .net asp.net-mvc-3 reflection asp.net-web-api


    【解决方案1】:

    public abstract bool CanWrite { get; }。该属性没有设置器..因此您的错误。

    在您的示例中,originalStream 的类型为 Stream。将其包装在另一个流中以供您测试。你不是在测试HttpRequest.InputStream,你是在测试反序列化......

    var originalStream = new StreamReader(HttpContext.Current.Request.InputStream);
    var content = originalStream.ReadToEnd();
    

    .. 等等

    您甚至可以考虑完全跳过使用请求输入,因为您并没有真正测试它。

    编辑:

    再扩展一点。您应该将其移出控制器操作.. 执行以下操作:

    public class YourController : Controller {
        private readonly IStreamWrapper _streamWrapper;
    
        public YourController(IStreamWrapper wrapper) {
            _streamWrapper = wrapper;
        }
    
        public ActionResult MethodYouAreTesting() {
            var result = _streamWrapper.Process(HttpRequest.InputStream);
        }
    }
    
    public class Tests {
        public void YourTestMethod() {
            var controller = new YourController(new FakeStreamWrapper()); // mock perhaps?
            // Asserts here for the controller action
        }
    
        public void YourWrapperTester() {
            var wrapper = new RealStreamWrapper();
            // test Process method here..
        }
    }
    

    然后就可以单独测试流读取和反序列化了。

    这有意义还是我让它变得更复杂了? :/

    【讨论】:

    • 我正在测试我的控制器的动作。为了获取请求正文,我的控制器执行 Context.Current.Request.InputStream.Read (基本上)所以我需要能够设置它。我无法将身体传递给我的行动。我不明白你的方法将如何工作。
    • 这是我的观点。我会将逻辑移到它自己的类中。将其注入控制器..并使用它来测试动作。您可以将InputStream 传递到这个单独的类中,该类将其包装在更具体的实现中。你正在做的事情比它需要的要困难得多。我会更新我的答案。
    • 谢谢,但是如何让 InputStream 读取我在测试方法中定义的内容?我明白你的方式是如何工作的,但我不明白如何告诉 InputStream 读取什么。
    • 在您的测试中不必如此。你的 fake/mock 可以实例化一个新的流来测试。在实际的实现中,它将使用InputStream(因为注入的依赖是可能的)。这些模拟实例将在您的测试期间传入空流、空流等。这很好(我认为),因为您没有测试流......您正在测试内容。所以无论是InputStream 还是在您的测试中.. 都无关紧要。
    • @SimonWhitehead 宣讲 - 这是完美的测试场景 - OP 正在测试太多的实现细节,只需要注入一些依赖项来处理这个问题
    【解决方案2】:

    InputStream 是一个只读流。

    有几种方法。从最简单的开始:

    创建 StreamReader 的实例并在 InputStream 上调用 ReadToEnd() 以获取正在发送的数据。

    获得内容后,您可以在进行 JSON 转换之前根据需要进行修改。

    同样,这是做你想做的最简单、最不优雅的方式,但它应该可以工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多