Pregunta

Finalmente comencé a jugar con la creación de algunas aplicaciones que funcionan con interfaces web RESTful, sin embargo, me preocupa estar dañando sus servidores cada vez que presiono F5 para ejecutar una serie de pruebas.

Básicamente, necesito obtener una serie de respuestas web para poder probar que estoy analizando las diferentes respuestas correctamente, en lugar de acceder a sus servidores cada vez. Pensé que podría hacer esto una vez, guardar el XML y luego trabajar localmente.

Sin embargo, no veo cómo puedo "burlarme" de una WebResponse, ya que (AFAIK) solo pueden ser instanciados por WebRequest.GetResponse

¿Cómo hacen ustedes para burlarse de este tipo de cosas?¿Tú?Realmente no me gusta el hecho de que estoy atacando sus servidores :S No quiero cambiar el código también mucho, pero espero que haya una forma elegante de hacer esto.

Actualizar Siguiendo Aceptar

La respuesta de Will fue la bofetada que necesitaba, ¡sabía que me estaba perdiendo un punto fundamental!

  • Cree una interfaz que devolverá un objeto proxy que represente el XML.
  • Implemente la interfaz dos veces, una que usa WebRequest y la otra que devuelve "respuestas" estáticas.
  • La implementación de la interfaz crea una instancia del tipo de retorno según la respuesta o el XML estático.
  • Luego puede pasar la clase requerida durante las pruebas o en producción a la capa de servicio.

Una vez que tenga el código creado, pegaré algunas muestras.

¿Fue útil?

Solución

Encontré esta pregunta mientras buscaba hacer exactamente lo mismo.No pude encontrar una respuesta en ninguna parte, pero después de investigar un poco más descubrí que .Net Framework tiene soporte integrado para esto.

Puede registrar un objeto de fábrica con WebRequest.RegisterPrefix cual WebRequest.Create llamará cuando use ese prefijo (o URL).El objeto de fábrica debe implementar IWebRequestCreate que tiene un solo método Create que devuelve un WebRequest.Aquí puedes devolver tu simulacro. WebRequest.

He puesto un código de muestra enhttp://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/

Otros consejos

Aquí hay una solución que no requiere burla.Implementas los tres componentes del WebRequest: IWebRequestCreate WebRequest y WebResponse.Vea abajo.Mi ejemplo genera solicitudes fallidas (al lanzar WebException), pero debería poder adaptarlo para enviar respuestas "reales":

class WebRequestFailedCreate : IWebRequestCreate {
    HttpStatusCode status;
    String statusDescription;
    public WebRequestFailedCreate(HttpStatusCode hsc, String sd) {
        status = hsc;
        statusDescription = sd;
    }
    #region IWebRequestCreate Members
    public WebRequest Create(Uri uri) {
        return new WebRequestFailed(uri, status, statusDescription);
    }
    #endregion
}
class WebRequestFailed : WebRequest {
    HttpStatusCode status;
    String statusDescription;
    Uri itemUri;
    public WebRequestFailed(Uri uri, HttpStatusCode status, String statusDescription) {
        this.itemUri = uri;
        this.status = status;
        this.statusDescription = statusDescription;
    }
    WebException GetException() {
        SerializationInfo si = new SerializationInfo(typeof(HttpWebResponse), new System.Runtime.Serialization.FormatterConverter());
        StreamingContext sc = new StreamingContext();
        WebHeaderCollection headers = new WebHeaderCollection();
        si.AddValue("m_HttpResponseHeaders", headers);
        si.AddValue("m_Uri", itemUri);
        si.AddValue("m_Certificate", null);
        si.AddValue("m_Version", HttpVersion.Version11);
        si.AddValue("m_StatusCode", status);
        si.AddValue("m_ContentLength", 0);
        si.AddValue("m_Verb", "GET");
        si.AddValue("m_StatusDescription", statusDescription);
        si.AddValue("m_MediaType", null);
        WebResponseFailed wr = new WebResponseFailed(si, sc);
        Exception inner = new Exception(statusDescription);
        return new WebException("This request failed", inner, WebExceptionStatus.ProtocolError, wr);
    }
    public override WebResponse GetResponse() {
        throw GetException();
    }
    public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state) {
        Task<WebResponse> f = Task<WebResponse>.Factory.StartNew (
            _ =>
            {
                throw GetException();
            },
            state
        );
        if (callback != null) f.ContinueWith((res) => callback(f));
        return f;
    }
    public override WebResponse EndGetResponse(IAsyncResult asyncResult) {
        return ((Task<WebResponse>)asyncResult).Result;
    }

}
class WebResponseFailed : HttpWebResponse {
    public WebResponseFailed(SerializationInfo serializationInfo, StreamingContext streamingContext)
        : base(serializationInfo, streamingContext) {
    }
}

Debes crear un HttpWebResponse subclase, porque de otro modo no se puede crear una.

La parte difícil (en el GetException() método) está ingresando los valores que no puede anular, p. StatusCode y aquí es donde nuestro mejor amigo SerializaionInfo ¡viene en!Aquí es donde usted proporciona los valores que no puede anular.Obviamente, anule las partes (de HttpWebResponse) podrás recorrer el resto del camino hasta allí.

¿Cómo obtuve los "nombres" en todos esos AddValue() llamadas?¡De los mensajes de excepción!Fue bastante amable contármelo uno por uno, hasta que lo hice feliz.

Ahora, el compilador se quejará de "obsoleto", pero aún así funciona, incluida la versión 4 de .NET Framework.

Aquí hay un caso de prueba (aprobado) como referencia:

    [TestMethod, ExpectedException(typeof(WebException))]
    public void WebRequestFailedThrowsWebException() {
        string TestURIProtocol = TestContext.TestName;
        var ResourcesBaseURL = TestURIProtocol + "://resources/";
        var ContainerBaseURL = ResourcesBaseURL + "container" + "/";
        WebRequest.RegisterPrefix(TestURIProtocol, new WebRequestFailedCreate(HttpStatusCode.InternalServerError, "This request failed on purpose."));
        WebRequest wr = WebRequest.Create(ContainerBaseURL);
        try {
            WebResponse wrsp = wr.GetResponse();
            using (wrsp) {
                Assert.Fail("WebRequest.GetResponse() Should not have succeeded.");
            }
        }
        catch (WebException we) {
            Assert.IsInstanceOfType(we.Response, typeof(HttpWebResponse));
            Assert.AreEqual(HttpStatusCode.InternalServerError, (we.Response as HttpWebResponse).StatusCode, "Status Code failed");
            throw we;
        }
    }

No puedes.Lo mejor que puedes hacer es envolverlo en un objeto proxy y luego burlarte de eso.Alternativamente, tendrías que usar un marco simulado que pueda interceptar tipos que no se pueden burlar, como TypeMock.Pero estás hablando de dólares.Es mejor envolver un poco.


Aparentemente tu poder con un poco de trabajo extra.Consulta aquí la respuesta más votada.

Encontré el siguiente blog anteriormente que explica un enfoque bastante bueno usando Microsoft Moles.

http://maraboustork.co.uk/index.php/2011/03/mocking-httpwebresponse-with-moles/

En resumen, la solución sugiere lo siguiente:

    [TestMethod]
    [HostType("Moles")]
    [Description("Tests that the default scraper returns the correct result")]
    public void Scrape_KnownUrl_ReturnsExpectedValue()
    {
        var mockedWebResponse = new MHttpWebResponse();

        MHttpWebRequest.AllInstances.GetResponse = (x) =>
        {
            return mockedWebResponse;
        };

        mockedWebResponse.StatusCodeGet = () => { return HttpStatusCode.OK; };
        mockedWebResponse.ResponseUriGet = () => { return new Uri("http://www.google.co.uk/someRedirect.aspx"); };
        mockedWebResponse.ContentTypeGet = () => { return "testHttpResponse"; }; 

        var mockedResponse = "<html> \r\n" +
                             "  <head></head> \r\n" +
                             "  <body> \r\n" +
                             "     <h1>Hello World</h1> \r\n" +
                             "  </body> \r\n" +
                             "</html>";

        var s = new MemoryStream();
        var sw = new StreamWriter(s);

            sw.Write(mockedResponse);
            sw.Flush();

            s.Seek(0, SeekOrigin.Begin);

        mockedWebResponse.GetResponseStream = () => s;

        var scraper = new DefaultScraper();
        var retVal = scraper.Scrape("http://www.google.co.uk");

        Assert.AreEqual(mockedResponse, retVal.Content, "Should have returned the test html response");
        Assert.AreEqual("http://www.google.co.uk/someRedirect.aspx", retVal.FinalUrl, "The finalUrl does not correctly represent the redirection that took place.");
    }

Esta no es una solución perfecta, pero ya me funcionó antes y merece un cuidado especial por su simplicidad:

HTTPSimulador

También un ejemplo de simulación tipográfica documentado en foros de simulación de tipos:

using System;
using System.IO;
using System.Net;
using NUnit.Framework;
using TypeMock;

namespace MockHttpWebRequest
{
  public class LibraryClass
  {
    public string GetGoogleHomePage()
    {
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.com");
      HttpWebResponse response = (HttpWebResponse)request.GetResponse();
      using (StreamReader reader = new StreamReader(response.GetResponseStream()))
      {
        return reader.ReadToEnd();
      }
    }
  }

  [TestFixture]
  [VerifyMocks]
  public class UnitTests
  {
    private Stream responseStream = null;
    private const string ExpectedResponseContent = "Content from mocked response.";

    [SetUp]
    public void SetUp()
    {
      System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
      byte[] contentAsBytes = encoding.GetBytes(ExpectedResponseContent);
      this.responseStream = new MemoryStream();
      this.responseStream.Write(contentAsBytes, 0, contentAsBytes.Length);
      this.responseStream.Position = 0;
    }

    [TearDown]
    public void TearDown()
    {
      if (responseStream != null)
      {
        responseStream.Dispose();
        responseStream = null;
      }
    }

    [Test(Description = "Mocks a web request using natural mocks.")]
    public void NaturalMocks()
    {
      HttpWebRequest mockRequest = RecorderManager.CreateMockedObject<HttpWebRequest>(Constructor.Mocked);
      HttpWebResponse mockResponse = RecorderManager.CreateMockedObject<HttpWebResponse>(Constructor.Mocked);
      using (RecordExpectations recorder = RecorderManager.StartRecording())
      {
        WebRequest.Create("http://www.google.com");
        recorder.CheckArguments();
        recorder.Return(mockRequest);

        mockRequest.GetResponse();
        recorder.Return(mockResponse);

        mockResponse.GetResponseStream();
        recorder.Return(this.responseStream);
      }

      LibraryClass testObject = new LibraryClass();
      string result = testObject.GetGoogleHomePage();
      Assert.AreEqual(ExpectedResponseContent, result);
    }

    [Test(Description = "Mocks a web request using reflective mocks.")]
    public void ReflectiveMocks()
    {
      Mock<HttpWebRequest> mockRequest = MockManager.Mock<HttpWebRequest>(Constructor.Mocked);
      MockObject<HttpWebResponse> mockResponse = MockManager.MockObject<HttpWebResponse>(Constructor.Mocked);
      mockResponse.ExpectAndReturn("GetResponseStream", this.responseStream);
      mockRequest.ExpectAndReturn("GetResponse", mockResponse.Object);

      LibraryClass testObject = new LibraryClass();
      string result = testObject.GetGoogleHomePage();
      Assert.AreEqual(ExpectedResponseContent, result);
    }
  }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top