从WebRequest模拟WebResponse
我终于开始创建一些可以与RESTful Web界面一起使用的应用程序了,但是,我担心每次我按F5来运行一系列测试时,我都会锤打他们的服务器。
基本上,我需要获得一系列Web响应,以便可以测试是否正确解析了各种响应,而不是每次都访问它们的服务器,我认为我可以这样做一次,保存XML,然后在本地工作。
但是,我看不到如何"模拟" WebResponse,因为(AFAIK)它们只能由WebRequest.GetResponse实例化。
你们如何嘲笑这种事情?你?我只是真的不喜欢我要锤击他们的服务器:S我不想过多更改代码,但我希望有一种优雅的方法。
接受后更新
威尔的答案是我需要拍打脸,我知道我错过了一个基本要点!
- 创建一个接口,该接口将返回代表XML的代理对象。
- 两次实现接口,一个接口使用WebRequest,另一个接口返回静态"响应"。
- 然后,接口实现或者根据响应实例化返回类型,或者静态XML。
- 然后,我们可以在测试或者生产时将所需的类传递给服务层。
敲完代码后,我将粘贴一些示例。
解决方案
回答
你不能最好的办法是将其包装在代理对象中,然后对其进行模拟。或者,我们必须使用一个模拟框架,该框架可以拦截无法模拟的类型,例如TypeMock。但是,我们在谈论的是雄鹿。最好做一点包裹。
显然,我们可以做一些额外的工作。在此处查看投票最高的答案。
回答
这不是一个完美的解决方案,但它以前对我有用,并且为简单起见值得特别注意:
HTTP模拟器
也是typemock论坛中记录的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); } } }
回答
我在寻找做同样的事情时发现了这个问题。在任何地方都找不到答案,但经过进一步挖掘后发现,.Net Framework已内置对此功能的支持。
我们可以使用WebRequest.RegisterPrefix
注册工厂对象,当使用该前缀(或者url)时,WebRequest.Create
将调用该对象。工厂对象必须实现" IWebRequestCreate",该方法具有一个返回" WebRequest"的单一方法" Create"。在这里,我们可以返回模拟WebRequest
。
我在上放了一些示例代码
http://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/