سؤال

لقد بدأت أخيرًا في إنشاء بعض التطبيقات التي تعمل مع واجهات الويب RESTful، ومع ذلك، أشعر بالقلق من أنني أضغط على خوادمهم في كل مرة أضغط فيها على F5 لإجراء سلسلة من الاختبارات.

في الأساس، أحتاج إلى الحصول على سلسلة من استجابات الويب حتى أتمكن من اختبار تحليل الاستجابات المتنوعة بشكل صحيح، بدلاً من الضغط على خوادمهم في كل مرة، اعتقدت أنه يمكنني القيام بذلك مرة واحدة، وحفظ ملف XML ثم العمل محليًا.

ومع ذلك، لا أرى كيف يمكنني "الاستهزاء" باستجابة WebResponse، نظرًا لأنه (AFAIK) لا يمكن إنشاء مثيل لها إلا من خلال WebRequest.GetResponse

كيف يا رفاق تسخرون من هذا النوع من الأشياء؟هل أنت؟أنا لا أحب حقًا حقيقة أنني أطرق خوادمهم: S لا أريد تغيير الكود أيضاً كثيرا، ولكن أتوقع أن هناك طريقة أنيقة للقيام بذلك..

تحديث بعد قبول

كانت إجابة ويل هي الصفعة التي كنت أحتاجها، كنت أعلم أنني كنت أفتقد نقطة أساسية!

  • قم بإنشاء واجهة تقوم بإرجاع كائن الوكيل الذي يمثل XML.
  • قم بتنفيذ الواجهة مرتين، إحداهما تستخدم WebRequest، والأخرى تستخدم "الاستجابات" الثابتة.
  • يقوم تنفيذ الواجهة بعد ذلك إما بإنشاء نوع الإرجاع بناءً على الاستجابة، أو XML الثابت.
  • يمكنك بعد ذلك تمرير الفئة المطلوبة عند الاختبار أو عند الإنتاج إلى طبقة الخدمة.

بمجرد أن أحصل على الكود، سألصق بعض العينات.

هل كانت مفيدة؟

المحلول

لقد وجدت هذا السؤال بينما كنت أتطلع إلى القيام بنفس الشيء تمامًا.لم أتمكن من العثور على إجابة في أي مكان، ولكن بعد المزيد من البحث وجدت أن .Net Framework قد بني دعمًا لذلك.

يمكنك تسجيل كائن المصنع باستخدام WebRequest.RegisterPrefix أيّ WebRequest.Create سيتم الاتصال عند استخدام تلك البادئة (أو عنوان url).يجب تنفيذ كائن المصنع IWebRequestCreate الذي له طريقة واحدة Create الذي يعود أ WebRequest.هنا يمكنك إرجاع صورتك الوهمية WebRequest.

لقد وضعت بعض نماذج التعليمات البرمجية فيhttp://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/

نصائح أخرى

إليك الحل الذي لا يتطلب السخرية.يمكنك تنفيذ جميع المكونات الثلاثة لل WebRequest: IWebRequestCreate WebRequest و WebResponse.انظر أدناه.يُنشئ المثال الخاص بي طلبات فاشلة (عن طريق رمي WebException)، ولكن يجب أن يكون قادرًا على تكييفه لإرسال استجابات "حقيقية":

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

يجب عليك إنشاء HttpWebResponse فئة فرعية، لأنه لا يمكنك إنشاء واحدة بطريقة أخرى.

الجزء الصعب (في GetException() الطريقة) يتم تغذية القيم التي لا يمكنك تجاوزها، على سبيل المثال. StatusCode وهذا هو المكان الذي هو أفضل صديق لنا SerializaionInfo ادخل!هذا هو المكان الذي تقوم فيه بتوفير القيم التي لا يمكنك تجاوزها.من الواضح أن تجاوز الأجزاء (من HttpWebResponse) أنت قادر على قطع بقية الطريق إلى هناك.

كيف حصلت على "الأسماء" في كل هؤلاء AddValue() المكالمات؟من رسائل الاستثناء!كان من اللطيف أن أخبرني بكل واحد على حدة، حتى أسعدته.

الآن، سوف يشكو المترجم من "قديم" ولكنه مع ذلك يعمل، بما في ذلك الإصدار 4 من .NET Framework.

فيما يلي حالة اختبار (ناجحة) للرجوع إليها:

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

لا يمكنك.أفضل ما يمكنك فعله هو تغليفه بكائن وكيل، ثم الاستهزاء به.وبدلاً من ذلك، سيتعين عليك استخدام إطار عمل وهمي يمكنه اعتراض الأنواع التي لا يمكن الاستهزاء بها، مثل TypeMock.لكنك تتحدث عن الدولارات، هناك.من الأفضل أن تفعل القليل من التغليف.


على ما يبدو أنت يستطيع مع القليل من العمل الإضافي.تحقق من الإجابة الأعلى تصويتًا هنا.

لقد وجدت المدونة التالية في وقت سابق والتي تشرح أسلوبًا رائعًا باستخدام Microsoft Moles.

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

باختصار الحل يقترح ما يلي:

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

هذا ليس الحل الأمثل ولكنه نجح معي من قبل ويستحق مزيدًا من الاهتمام للبساطة:

HTTPSimulator

تم أيضًا توثيق مثال على typemock في منتديات الكتابة:

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);
    }
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top