Sunday, April 17, 2011

Mocking WebResponse's from a WebRequest

I have finally started messing around with creating some apps that work with RESTful web interfaces, however, I am concerned that I am hammering their servers every time I hit F5 to run a series of tests..

Basically, I need to get a series of web responses so I can test I am parsing the varying responses correctly, rather than hit their servers every time, I thought I could do this once, save the XML and then work locally.

However, I don't see how I can "mock" a WebResponse, since (AFAIK) they can only be instantiated by WebRequest.GetResponse

How do you guys go about mocking this sort of thing? Do you? I just really don't like the fact I am hammering their servers :S I dont want to change the code too much, but I expect there is a elegant way of doing this..

Update Following Accept

Will's answer was the slap in the face I needed, I knew I was missing a fundamental point!

  • Create an Interface that will return a proxy object which represents the XML.
  • Implement the interface twice, on that uses WebRequest, the other that returns static "responses".
  • The interface implmentation then either instantiates the return type based on the response, or the static XML.
  • You can then pass the required class when testing or at production to the service layer.

Once I have the code knocked up, I'll paste some samples.

Thanks Will :)

From stackoverflow
  • You can't. Best thing to do is wrap it in a proxy object, and then mock that. Alternatively, you'd have to use a mock framework that can intercept types that can't be mocked, like TypeMock. But you're talking about bucks, there. Better to do a little wrapping.


    Apparently you can with a little extra work. Check the highest voted answer here.

    Rob Cooper : Could you please clarify on how I could "wrap" without affecting the core code? As soon as that GetResponse is called, boom! off it runs.. Obviously I need to do some dependancy injection, but dont see how I can get to the object I need, you know?
    Rob Cooper : Belay that, being an idiot. Im with you. Create proxy object to carry the result, an interface for the calls. Implement in two classes: 1 = WebRequest, 2 = Static Objects). Makes sense now, thanks for slapping me in the face and making me see sense, I knew I couldnt see the wood through the trees :)
    tmont : this is incorrect, as richard willis' answer below shows how it can be done. WebResponse's CAN be mocked using the code he links to.
    Will : @tmont interesting and +1 to Willis. Edited for clarity. Nice they hid it well enough...
  • Instead of a true "mock", what if you just created a few local web services/methods on your server that return the data you need? Involves a bit more work than setting up a mock or test I imagine, but should be able to give you what you need.

    So your WebRequest.GetResponse would call out to something like "http://localhost/mytestservice.aspx" or something similar and that page would respond in whatever fashion you require.

    I'm probably missing your requirements or over-simplifying, but that's probably how I would approach it :)

    Rob Cooper : This is something I have considered, but it involves an fair bit of work to do (for the sake of testing).. I dont think you are missing or over-simplifying, just a something I would like to avoid, if possible. If not, then I need to either lose some concience or time :D
    Steve Dunn : Then the tests will fail for a multitude of other reasons even though the logic in the test might be fine. This is why unit tests shouldn't touch anything else, even local web services.
  • This is not a perfect solution yet it worked for me before and deserves extra care for the simplicity :

    HTTPSimulator

    Also a typemock example documented in typemock forums:

    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);
        }
      }
    }
    
  • I found this question while looking to do exactly the same thing. Couldn't find an answer anywhere, but after a bit more digging found that the .Net Framework has built in support for this.

    You can register a factory object with WebRequest.RegisterPrefix which WebRequest.Create will call when using that prefix (or url). The factory object must implement IWebRequestCreate which has a single method Create which returns a WebRequest. Here you can return your mock WebRequest.

    I've put some sample code up at http://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/

    tmont : +1 this should be the accepted answer. worked perfectly for me.

0 comments:

Post a Comment