Ответ 1
Вы не можете создать mock для HttpWebResponse, вместо того, чтобы издеваться над HttpWebResponse, это вызов wraping за интерфейсом и макет этого интерфейса.
Как создать mock объект HttpWebRequest и HttpWebResponse Я пытаюсь unittest следующий фрагмент кода
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);
}
Вы не можете создать mock для HttpWebResponse, вместо того, чтобы издеваться над HttpWebResponse, это вызов wraping за интерфейсом и макет этого интерфейса.
Это старый вопрос, но, возможно, он может быть полезен для кого-то. Адаптирован из этот вопрос в 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 или оставить его нулевым.
В проекте, над которым я работал, мне нужно было сделать некоторые переопределения в веб-службе ASMX, которая бросала ошибки только в производстве, чтобы узнать, какой 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;
}
Я не использовал его лично, но Moles framework должен позволить вам делать то, что вы пытаетесь сделать. см. здесь для примера, который перехватывает вызовы на DateTime.Now
.
Макет 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 addon HttpRequester довольно круто. Вы можете сделать все типы запросов, и вы тоже увидите ответ. Его удобный материал, который я бы сказал
Вы также можете использовать форматтер SOAP для построения макета HttpWebRequest. Сначала запустите HttpWebRequest, который вы хотите макетировать как строку, затем вы можете настроить его в своем unit test и перестроить 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)));
}
Вы можете использовать отражения для перезаписи полей ответа, которые необходимо настроить для создания макетного ответа. Вот пример создания ответа с кодом состояния 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;