如何为C#中使用WebRequest的HandleRequest方法编写单元测试
HandleRequest Method in Your C# Code Hey there! Let's break down how to write solid unit tests for this HandleRequest method. The key here is to isolate the method from external dependencies (like actual HTTP requests) so we can test just your business logic without hitting real endpoints.
First: Make Dependencies Mockable
Looking at your code, there are a few dependencies we need to abstract to make testing feasible:
_webRequestWrapper: This should be an interface (e.g.,IWebRequestWrapper) instead of a concrete class. Using an interface lets us use mocking frameworks like Moq or NSubstitute to create fake implementations for testing.ipnContext: Since you're accessingipnContext.RequestBody, this should also be abstracted into an interface (e.g.,IIpnContext). This lets us control exactly what request body gets passed in during tests.- Private
FormRequestmethod: Right now it creates aWebRequestdirectly. If you want to test the request string it uses, consider extracting this logic into a separate service (likeIRequestBuilder) that you inject. This avoids having to mess with reflection to test private code.
Core Test Scenarios & Examples
We'll use Moq for these examples (it's the most popular mocking library for .NET), but the concepts apply to any mocking tool.
Test 1: Verify Successful Response Assignment
This test checks that when the web request returns a valid response, the method correctly sets model.Verification.
using Moq; using Xunit; using System.IO; using System.Net; public class WebhookHandlerTests { [Fact] public void HandleRequest_ValidResponse_SetsVerificationCorrectly() { // Arrange: Set up test data and mocks var testRequestBody = "payment_id=123&status=completed"; var expectedVerification = "VERIFIED"; var testModel = new WebhookModel(); // Mock the IPN context to return our test request body var mockIpnContext = new Mock<IIpnContext>(); mockIpnContext.Setup(ctx => ctx.RequestBody).Returns(testRequestBody); // Mock the web response stream with our expected verification value var responseStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(expectedVerification)); var mockWebResponse = new Mock<WebResponse>(); mockWebResponse.Setup(resp => resp.GetResponseStream()).Returns(responseStream); // Mock the web request wrapper to return our fake response var mockWebRequestWrapper = new Mock<IWebRequestWrapper>(); mockWebRequestWrapper.Setup(w => w.GetRequestStream()).Returns(new MemoryStream()); // Empty stream for writing mockWebRequestWrapper.Setup(w => w.GetResponse()).Returns(mockWebResponse.Object); // Create the handler with our mocked dependencies var handler = new WebhookHandler(mockWebRequestWrapper.Object, mockIpnContext.Object); // Act: Run the method we're testing handler.HandleRequest(testModel); // Assert: Check that the verification was set correctly Assert.Equal(expectedVerification, testModel.Verification); // Verify that our wrapper methods were called exactly once (to ensure no missing steps) mockWebRequestWrapper.Verify(w => w.GetRequestStream(), Times.Once); mockWebRequestWrapper.Verify(w => w.GetResponse(), Times.Once); } }
Test 2: Validate the Request String Is Built Correctly
We need to ensure the method builds the right validation request string (cmd=_notify-validate&[requestBody]). If you've extracted FormRequest into an IRequestBuilder service, this becomes straightforward:
[Fact] public void HandleRequest_BuildsCorrectValidationRequestString() { // Arrange var testRequestBody = "foo=bar&baz=qux"; var expectedRequestString = "cmd=_notify-validate&foo=bar&baz=qux"; var mockIpnContext = new Mock<IIpnContext>(); mockIpnContext.Setup(ctx => ctx.RequestBody).Returns(testRequestBody); var mockRequestBuilder = new Mock<IRequestBuilder>(); mockRequestBuilder.Setup(builder => builder.BuildValidationRequest(It.Is<string>(s => s == expectedRequestString))) .Returns(Mock.Of<WebRequest>()); var mockWebRequestWrapper = new Mock<IWebRequestWrapper>(); // Set up wrapper mocks as before (empty request stream, etc.) var handler = new WebhookHandler(mockWebRequestWrapper.Object, mockIpnContext.Object, mockRequestBuilder.Object); // Act handler.HandleRequest(new WebhookModel()); // Assert: Verify the builder got the correct request string mockRequestBuilder.Verify(builder => builder.BuildValidationRequest(expectedRequestString), Times.Once); }
Test 3: Handle Exception Scenarios
Don't forget to test what happens when things go wrong (like a failed web request):
[Fact] public void HandleRequest_WebRequestThrowsException_HandlesGracefully() { // Arrange var mockWebRequestWrapper = new Mock<IWebRequestWrapper>(); mockWebRequestWrapper.Setup(w => w.GetResponse()).Throws(new WebException("Failed to reach verification endpoint")); var handler = new WebhookHandler(mockWebRequestWrapper.Object, Mock.Of<IIpnContext>()); var testModel = new WebhookModel(); // Act & Assert: Verify the method handles the exception (adjust based on your error handling logic) // If your method throws the exception, use this: Assert.Throws<WebException>(() => handler.HandleRequest(testModel)); // If your method catches the exception and sets an error state, use this instead: // handler.HandleRequest(testModel); // Assert.Equal("INVALID", testModel.Verification); }
Key Best Practices
- Don't test third-party code: You don't need to test that
StreamReader.ReadToEnd()orWebRequestworks—those are maintained by Microsoft and already tested. Focus on your logic. - Dependency Injection is non-negotiable: If your class is directly instantiating dependencies (like
new WebRequestWrapper()), refactor to inject them via the constructor. This is the foundation of testable code. - Avoid testing private methods: Instead, test the public behavior of
HandleRequestto indirectly validate that private logic works. If private code is complex enough to need its own tests, it's probably time to extract it into a separate service.
内容的提问来源于stack exchange,提问作者MrTKer

