Domanda

Ho finalmente iniziato a spippolare con la creazione di alcune applicazioni con interfacce RESTful web, tuttavia, mi preoccupa il fatto che sto martellando i loro server ogni volta che devo premere F5 per eseguire una serie di test..

Fondamentalmente, ho bisogno di ottenere una serie di risposte web così posso testare io sono analisi la variabile risposte correttamente, piuttosto che colpire i loro server ogni volta, ho pensato che avrei potuto fare questo una volta, salvare il file XML e quindi di lavorare a livello locale.

Tuttavia, non vedo come posso "finta", un WebResponse, dal momento che (per quanto ne sappia) possono solo essere creata un'istanza di WebRequest.GetResponse

Come fai ragazzi vai su beffardo una cosa di questo tipo?E tu?A me non piace il fatto che mi sto martellando i loro server :S io non voglio modificare il codice troppo molto, ma mi aspetto che ci sia un modo elegante per fare questo..

Aggiornamento A Seguito Di Accettare

Sarà la risposta è stata la schiaffo in faccia di cui avevo bisogno, ho capito che mi mancava un punto fondamentale!

  • Creare un'Interfaccia che restituirà un oggetto proxy, che rappresenta l'XML.
  • Implementare l'interfaccia per due volte, uno che utilizza WebRequest, altro che restituisce statico "risposte".
  • L'interfaccia di un'implementazione, quindi, crea il tipo di ritorno in base alla risposta, o l'XML statico.
  • È quindi possibile passare la classe in sede di collaudo o alla produzione per il livello di servizio.

Una volta che ho il codice incinta, ti incolla di alcuni campioni.

È stato utile?

Soluzione

Ho trovato questo problema, mentre cerca di fare esattamente la stessa cosa.Non riuscivo a trovare una risposta da nessuna parte, ma dopo un po ' di più a scavare trovato che l' .Net Framework ha un supporto incorporato per questo.

È possibile registrare una fabbrica di oggetti con WebRequest.RegisterPrefix che WebRequest.Create verrà chiamata quando si utilizza il prefisso (o url).La fabbrica oggetto deve implementare IWebRequestCreate che ha un solo metodo Create che restituisce un WebRequest.Qui potete restituire il vostro finto WebRequest.

Ho messo qualche esempio di codice fino a http://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/

Altri suggerimenti

Qui è una soluzione che non richiede beffardo.Implementare tutti e tre i componenti del WebRequest: IWebRequestCreate WebRequest e WebResponse.Vedere di seguito.Il mio esempio si genera in mancanza di richieste (da buttare WebException), ma dovrebbe essere in grado di adattare per l'invio di "reale" di risposte:

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) {
    }
}

È necessario creare un HttpWebResponse sottoclasse, in quanto non sarebbe stato altrimenti possibile crearne uno.

La parte difficile (in GetException() metodo) è l'alimentazione che i valori non si può ignorare, ad esempio, StatusCode e questo è dove il nostro migliore amico SerializaionInfo entra!Questo è dove si forniscono i valori non possono ignorare.Ovviamente, ignora le parti (di HttpWebResponse è possibile, per ottenere il resto della strada.

Come ho fatto a ottenere i "nomi" in tutte quelle AddValue() chiamate?Dai messaggi di eccezione!È stato bello abbastanza per dire a me ogni volta, fino a quando mi ha reso felice.

Ora, il compilatore si lamenta "obsoleti", ma questo, tuttavia, di opere, tra .NET Framework versione 4.

Qui è una (di passaggio) test case per riferimento:

    [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;
        }
    }

Non si può.Cosa migliore da fare è avvolgere in un oggetto proxy, e quindi finto che.In alternativa, si deve usare un finto quadro che è in grado di intercettare i tipi che non può essere preso in giro, come TypeMock.Ma tu stai parlando di dollari, c'.Meglio fare un po ' di confezionamento.


A quanto pare si può con un po ' di lavoro extra.Controllare il più alto votato risposta qui.

Ho trovato il seguente blog in precedenza, che spiega molto bene utilizzando l'approccio di Microsoft Talpe.

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

In breve la soluzione suggerisce il seguente:

    [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.");
    }

Questa non è una soluzione perfetta ma ha funzionato per me prima e merita particolare attenzione per la semplicità :

HTTPSimulator

Anche un typemock esempio documentato in typemock forum:

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);
    }
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top