12

如何创建模拟 HttpWebRequest 和 HttpWebResponse 对象我正在尝试对以下代码进行单元测试

HttpWebrequest request;   

if (null != request)
{                         
    var response = (HttpWebResponse)request.GetResponse();
    using (var sr = new StreamReader(response.GetResponseStream()))
    {
        jsonResult = sr.ReadToEnd();
    }

    var myRecords = SerializationHelper.Deserialize<Records>(jsonResult);
}
4

8 回答 8

2

您不能为 HttpWebResponse 创建模拟,而不是模拟 HttpWebResponse 是将调用包装在接口后面,然后模拟该接口。

于 2013-04-22T23:58:38.007 回答
2

这是一个老问题,但也许它对某人有用。改编自msdn 中的这个问题

private static WebResponse CreateWebResponse(HttpStatusCode httpStatus, MemoryStream responseObject)
{
  TcpListener l = new TcpListener(IPAddress.Loopback, 0);
  l.Start();
  int port = ((IPEndPoint)l.LocalEndpoint).Port;
  l.Stop();

  // Create a listener.
  string prefix = "http://localhost:" + port + "/";
  HttpListener listener = new HttpListener();
  listener.Prefixes.Add(prefix);
  listener.Start();
  try
  {
    listener.BeginGetContext((ar) =>
    {
      HttpListenerContext context = listener.EndGetContext(ar);
      HttpListenerRequest request = context.Request;

      // Obtain a response object.
      HttpListenerResponse response = context.Response;

      response.StatusCode = (int)httpStatus;

      // Construct a response.
      if (responseObject != null)
      {
        byte[] buffer = responseObject.ToArray();

        // Get a response stream and write the response to it.
        Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);
      }

      response.Close();
    }, null);

    WebClient client = new WebClient();
    try
    {
      WebRequest request = WebRequest.Create(prefix);
      request.Timeout = 30000;
      return request.GetResponse();
    }
    catch (WebException e)
    {
      return e.Response;
    }
  }
  finally
  {
    listener.Stop();
  }

  return null;
}

在这里,您可以在 responseObject 中编写任何您想要的内容或将其保留为空。

于 2014-07-29T12:33:32.343 回答
1

您可以使用反射来重写您需要调整的响应字段以创建模拟响应。以下是使用状态码 429 创建响应的示例:

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
statusCode = (int)response.StatusCode;
ActivateCallback(responseCallback, response, url, string.Empty);

var fieldStatusCode = response.GetType().GetField("m_StatusCode", 
                                                   BindingFlags.Public | 
                                                   BindingFlags.NonPublic | 
                                                   BindingFlags.Instance);
var statusCodeNew = (HttpStatusCode)409;
fieldStatusCode.SetValue(response, statusCodeNew);
string responceBody = "{\"error\":{\"code\":\"AF429\",\"message\":\"Too many requests. Method=GetContents, PublisherId=00000000-0000-0000-0000-000000000000\"}}";

var propStream = response.GetType().GetField("m_ConnectStream", 
                                              BindingFlags.Public | 
                                              BindingFlags.NonPublic | 
                                              BindingFlags.Instance);

System.IO.MemoryStream ms = new System.IO.MemoryStream(
                           System.Text.Encoding.UTF8.GetBytes(responceBody));
propStream.SetValue(response, ms);
ms.Position = 0;
于 2017-06-12T05:59:11.667 回答
1

在我一直从事的一个项目中,我需要在一个仅在生产中引发错误的 ASMX Web 服务中进行一些覆盖,以找出该服务实际上正在流回什么 XML。为此,我必须创建一个新的 HttpWebResponse 对象。基本技巧是使用Activator.CreateInstance(它绕过了构造函数已被弃用的事实)。在下面的示例中,我利用了这样一个事实,即我只是克隆现有的 HttpWebResponse 对象并重置流,但是完全从头开始创建一个的技术是相同的。

    string sLastXML;

    public string LastXML
    {
        get
        {
            return sLastXML;
        }
    }

    protected override System.Net.WebResponse GetWebResponse(System.Net.WebRequest request)
    {
        // Get the XML Returned
        System.Net.HttpWebResponse oResponse = (System.Net.HttpWebResponse)request.GetResponse();
        System.IO.Stream oStream = oResponse.GetResponseStream();
        byte[] inStream = new byte[oResponse.ContentLength];
        int iActual = 0;
        while (iActual < oResponse.ContentLength)
        {
            iActual += oStream.Read(inStream, iActual, (int)oResponse.ContentLength - iActual);
        }
        sLastXML = System.Text.Encoding.Default.GetString(inStream);

        // Create new stream
        System.IO.MemoryStream oNewStream = new System.IO.MemoryStream();
        oNewStream.Write(inStream, 0, (int)oResponse.ContentLength);
        oNewStream.Position = 0;

        // Create new response object
        System.Net.HttpWebResponse oNewResponse = (System.Net.HttpWebResponse)System.Activator.CreateInstance(typeof(System.Net.HttpWebResponse));
        System.Reflection.PropertyInfo oInfo = oNewResponse.GetType().GetProperty("ResponseStream", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oInfo.SetValue(oNewResponse,oNewStream);
        System.Reflection.FieldInfo oFInfo = oNewResponse.GetType().GetField("m_HttpResponseHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_ContentLength", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_Verb", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_StatusCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_StatusDescription", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_IsMutuallyAuthenticated", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_IsVersionHttp11", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_MediaType", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_Uri", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));
        oFInfo = oNewResponse.GetType().GetField("m_UsesProxySemantics", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
        oFInfo.SetValue(oNewResponse, oFInfo.GetValue(oResponse));

        oNewResponse.Cookies = oResponse.Cookies;

        return oNewResponse;


    }
于 2016-03-11T17:08:50.740 回答
0

模拟 HttpResponse 可能如下所示:

public SenHttpMockResponse(HttpListenerContext context)
{
   HttpListenerResponse response = context.Response;
   response.Headers.Add("Content-type", @"application/json");
   JObject message = JObject.Parse(@"{'SomeParameterName':'ParameterValue'}");
   StreamWriter writer = new StreamWriter(response.OutputStream);
   writer.Write(message);
   writer.Close();
}

在这种情况下,默认响应代码为 200,但您也可以使用Response.StatusCode = 400;

要模拟一个请求,您可以使用例如调试器工具。例如,Firefox 插件HttpRequester 非常酷。您可以提出所有类型的请求,您也会看到您的回复。我会说这是一个方便的东西

于 2014-07-31T09:10:51.093 回答
0

我没有亲自使用过它,但是Moles框架应该可以让你做你想做的事情。有关拦截DateTime.Now.

于 2013-04-26T20:38:52.270 回答
0

您还可以使用 SOAP 格式化程序来构造一个模拟 HttpWebRequest。首先将要模拟的 HttpWebRequest 捕获为字符串,然后您可以在单元测试中对其进行自定义,并通过反序列化字符串来重建 HttpWebRequest。

这是一个例子:

    static void Main(string[] args)
    {
        SoapFormatter formatter = new SoapFormatter();

        HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create("http://www.google.com");
        HttpWebResponse resp = (HttpWebResponse)myReq.GetResponse();

        // save this soapRequest as a string and customize it for your mocking up
        MemoryStream target = new MemoryStream();
        using(target)
        {
            formatter.Serialize(target, resp);
        }
        string soapRequest = Encoding.UTF8.GetString(target.GetBuffer());

        // now you can use the string to reconstruct the object from the string without needing anything special (other than substituting your own values into the XML)
        HttpWebResponse myMockedObject = (HttpWebResponse)formatter.Deserialize(
            new MemoryStream(Encoding.UTF8.GetBytes(soapRequest)));
    }
于 2014-08-20T15:48:20.687 回答
0

我认为包装和抽象是最正确的方法。但是,这是我的 hacky 解决方案。

只需使用序列化构造函数重载(它也已过时,但会生成可以抑制的警告)并覆盖您需要更改的方法。

public class FakeHttpWebRequest : HttpWebRequest
{
    private static readonly SerializationInfo SerializationInfo = GetSerializationInfo();
    private readonly WebResponse response;

    public FakeHttpWebRequest(WebResponse response)
    #pragma warning disable CS0618 // Type or member is obsolete
        : base(
              SerializationInfo,
              new StreamingContext())
    #pragma warning restore CS0618 // Type or member is obsolete

    {
        this.response = response;
    }

    public override WebResponse GetResponse()
    {
        return response;
    }

    public override Stream GetRequestStream()
    {
        return new MemoryStream();
    }

    private static SerializationInfo GetSerializationInfo()
    {
        // dummy data required for HttpWebRequest() constructor
        var serializationInfo = new SerializationInfo(typeof(HttpWebRequest), new FormatterConverter());
        serializationInfo.AddValue("_HttpRequestHeaders", new WebHeaderCollection(), typeof(WebHeaderCollection));
        serializationInfo.AddValue("_Proxy", null, typeof(IWebProxy));
        serializationInfo.AddValue("_KeepAlive", false);
        serializationInfo.AddValue("_Pipelined", false);
        serializationInfo.AddValue("_AllowAutoRedirect", false);
        serializationInfo.AddValue("_AllowWriteStreamBuffering", false);
        serializationInfo.AddValue("_HttpWriteMode", 0);
        serializationInfo.AddValue("_MaximumAllowedRedirections", 0);
        serializationInfo.AddValue("_AutoRedirects", 0);
        serializationInfo.AddValue("_Timeout", 0);
        serializationInfo.AddValue("_ContentLength", (long)0);
        serializationInfo.AddValue("_MediaType", "");
        serializationInfo.AddValue("_OriginVerb", "GET");
        serializationInfo.AddValue("_ConnectionGroupName", "");
        serializationInfo.AddValue("_Version", new Version(1, 0), typeof(Version));
        serializationInfo.AddValue("_OriginUri", new Uri("https://fake.uri"), typeof(Uri));

        return serializationInfo;
    }
}


public class FakeHttpWebResponse : HttpWebResponse
{
    private static readonly SerializationInfo SerializationInfo = GetSerializationInfo();
    private readonly Stream responseStream;

    public FakeHttpWebResponse(HttpStatusCode statusCode, Stream responseStream)
    #pragma warning disable CS0618 // Type or member is obsolete
        : base(
              SerializationInfo,
              new StreamingContext())
    #pragma warning restore CS0618 // Type or member is obsolete
    {
        this.StatusCode = statusCode;
        this.responseStream = responseStream;
    }

    public override HttpStatusCode StatusCode { get; }

    public override Stream GetResponseStream()
    {
        return responseStream;
    }

    private static SerializationInfo GetSerializationInfo()
    {
        // dummy data required for HttpWebResponse() constructor
        var serializationInfo = new SerializationInfo(typeof(HttpWebResponse), new FormatterConverter());
        serializationInfo.AddValue("m_HttpResponseHeaders", new WebHeaderCollection(), typeof(WebHeaderCollection));
        serializationInfo.AddValue("m_Uri", new Uri("https://fake.uri"), typeof(Uri));
        serializationInfo.AddValue("m_Certificate", null, typeof(System.Security.Cryptography.X509Certificates.X509Certificate));
        serializationInfo.AddValue("m_Version", new Version(), typeof(Version));
        serializationInfo.AddValue("m_StatusCode", (int)HttpStatusCode.HttpVersionNotSupported);
        serializationInfo.AddValue("m_ContentLength", (long)0);
        serializationInfo.AddValue("m_Verb", "GET");
        serializationInfo.AddValue("m_StatusDescription", "");
        serializationInfo.AddValue("m_MediaType", "");
        return serializationInfo;
    }
}

最后,在您的生产代码中提供一种方法来将 WebRequest 创建替换为您的测试代码。

    /// <summary>
    /// Delegate to create new web request. Can be set to mock actual requests.
    /// </summary>
    public Func<string, WebRequest> CreateWebRequest { get; set; } = url => WebRequest.Create(url);

并从您的生产代码中使用此委托,而不是直接使用 WebRequest.Create()。

在测试中,您可以将此委托设置为您需要的任何内容。

于 2021-04-08T21:11:52.713 回答