Frage

Ich habe begonnen, schließlich Herumspielen einige Anwendungen mit der Erstellung, die mit RESTful Web-Schnittstellen arbeiten, aber ich bin besorgt darüber, dass ich ihre Server ich F5 getroffen jedes Mal bin hämmern eine Reihe von Tests ausgeführt werden ..

Im Grunde genommen, ich brauche eine Reihe von Web-Antworten zu bekommen, damit ich testen kann ich die unterschiedlichen Antworten richtig bin Parsen, anstatt ihre Server jedes Mal treffe, dachte ich mir dies einmal tun könnte, das XML-Format speichern und dann lokal arbeiten.

Allerdings sehe ich nicht, wie ich kann "Mock" ein WebResponse, da (AFAIK) sie können nur von instanziert werden WebRequest.GetResponse

Wie geht ihr über diese Art der Sache verspotten? Machst du? Ich habe wirklich nicht mag die Tatsache, dass ich ihren Server bin hämmern: S Ich mag nicht den Code ändern zu viel, aber ich erwarte, dass es eine elegante Art und Weise, dies zu tun ..

Update akzeptieren folgende

Wills Antwort war der Schlag ins Gesicht ich brauchte, ich wusste, dass ich einen grundlegenden Punkt fehle!

  • Erstellen Sie eine Schnittstelle, die ein Proxy-Objekt zurückgeben, die die XML darstellt.
  • Implementieren Sie die Schnittstelle zweimal, eine, die WebRequest verwendet, die andere, die statischen „Antworten“ zurück.
  • Die Schnittstelle implmentation dann instanziiert entweder den Rückgabetyp auf der Antwort basiert, oder die statische XML.
  • Sie können dann die gewünschte Klasse übergeben, wenn sie bei der Produktion der Dienstschicht zu testen oder.

Wenn ich den Code oben geklopft haben, ich werde einige Beispiele einfügen.

War es hilfreich?

Lösung

Ich fand diese Frage beim Schauen genau das Gleiche zu tun. Es kann keine Antwort überall, aber nach etwas mehr graben gefunden feststellen, dass die .Net Framework Unterstützung für diesen gebaut hat.

Sie können ein Factory-Objekt mit WebRequest.RegisterPrefix registrieren, die WebRequest.Create nennen, wenn diese Präfix (oder URL) verwenden. Die Fabrik-Objekt muss IWebRequestCreate implementieren, die eine einzige Methode Create hat, die eine WebRequest zurückgibt. Hier können Sie Ihre Mock WebRequest zurück.

Ich habe einige Beispiel-Code setzen bei http: // blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/

Andere Tipps

Hier ist eine Lösung, die nicht spöttisch erfordert. Sie implementieren alle drei Komponenten des WebRequest: IWebRequestCreate WebRequest und WebResponse. Siehe unten. Mein Beispiel erzeugt Anfragen (durch das Werfen WebException) versagt, aber sollte es anpassen können „echte“ Antworten senden:

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

Sie müssen eine HttpWebResponse Unterklasse erstellen, da Sie sonst nicht erstellen kann.

Der schwierige Teil (in der GetException() Methode) in den Werten füttern Sie nicht außer Kraft setzen können, beispielsweise StatusCode und das ist, wo unsere bestest Kumpel SerializaionInfo kommt! Hier können Sie die Werte liefern können Sie nicht außer Kraft setzen. Offensichtlich die Teile außer Kraft setzen (von HttpWebResponse) Sie sind in der Lage, den Rest des Weges dorthin zu gelangen.

Wie haben erhalte ich die „Namen“ in all diesen AddValue() Anrufe? Von den Ausnahmemeldungen! Es war schön genug, um mir wiederum ein jeder zu sagen, bis ich es glücklich gemacht.

Nun wird der Compiler beschweren sich über "veraltet", aber das funktioniert trotzdem, einschließlich .NET Framework Version 4.

Hier

ein (passing) Testfall als Referenz:

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

Sie können nicht. Das Beste, was zu tun ist, wickeln Sie es in einem Proxy-Objekt, und dann, dass verspotten. Alternativ würden Sie einen Mock Rahmen verwenden, um die Typen abfangen können, die sich nicht spotten kann, wie TypeMock. Aber Sie sprechen Dollar gibt. Besser ein wenig Verpackung zu tun.


Offenbar Sie können mit ein wenig mehr Arbeit. Schauen Sie sich die höchste gestimmt Antwort hier.

fand ich den folgenden Blog früher die ganz einen schönen Ansatz mit Microsoft Moles erklärt.

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

kurz in der Lösung schlägt vor, die folgenden:

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

Dies ist keine perfekte Lösung, aber es für mich gearbeitet und verdient für die Einfachheit besonders vorsichtig:

HTTPSimulator

Auch ein Typemock Beispiel dokumentiert in Typemock Foren :

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);
    }
  }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top