我的当前基于Asp.net的项目在处理各种请求时大量使用Http处理程序。那么,是否有任何方法可以使用单元测试用例测试每个处理程序的功能?我们正在使用Nunit和Moq框架来实现单元测试。
我的当前基于Asp.net的项目在处理各种请求时大量使用Http处理程序。那么,是否有任何方法可以使用单元测试用例测试每个处理程序的功能?我们正在使用Nunit和Moq框架来实现单元测试。
我认为一些以前的博客文章与此相关:
http://www.kongsli.net/nblog/2009/05/03/aspnet-35-improving-testability-with-systemwebabstractions/
请参见第一篇帖子中的示例#2,了解如何对HttpHandler进行单元测试的示例。如果您不关心单元测试,想要快速而简单的解决方案,可以使用Fiddler
如果您希望采用更加集成的方法(单元测试),可以使用WebRequest和WebResponse。
当然可以,虽然我自己还没有“愤怒地”这样做过。
使用System.Net.WebClient对处理程序进行HTTP调用,并评估返回的内容,这将允许您测试处理程序的公共界面。
在此示例中,我已经硬编码了我的目标,并且我正在使用WebClient上的一个方法来返回一个字符串。
WebClient还提供了访问ResponseHeaders、Encoding和其他有用的“网络”信息的功能;您也可以上传信息。
using System.Net;
namespace UnitTestHttpHandler
{
public class TestHarness
{
public static string GetString()
{
WebClient myWebClient = new WebClient();
return myWebClient.DownloadString("http://localhost/Morphfolia.Web/ContentList.ashx");
}
}
}
然后,您可以使用TestHarness调用目标HttpHandler并在测试中验证结果(或者如果您知道更好的测试方法,则可以使用更好的方法进行测试 - 我不是单元测试大师)。
[TestMethod]
public void TestMethod1()
{
string x = UnitTestHttpHandler.TestHarness.GetString();
Assert.IsTrue(x.Length > 5);
}
IHttpHandler
的默认接口不可测试,因为 ProcessRequest(HttpContext context)
的参数无法进行模拟。
本答案受到这篇文章的启发。
为使您的 IHttpHandler
实现可测试,您必须先进行一些小改变,以便使用可模拟的 HttpContextBase
:
原文:
class YourHttpHandler : IHttpHandler
{
public bool IsReusable => true;
public void ProcessRequest(HttpContext context)
{
/* Your handler implementation */
}
}
致:
class YourHttpHandler : IHttpHandler
{
public bool IsReusable => true;
public void ProcessRequest(HttpContext context)
=> ProcessRequest(new HttpContextWrapper(context));
public virtual void ProcessRequest(HttpContextBase context)
{
/* Your handler implementation */
}
}
ProcessRequest(HttpContext context)
移动到ProcessRequest(HttpContextBase context)
。ProcessRequest(HttpContextBase context)
方法,在测试中验证其中的功能。YourHttpHandler
,发送模拟请求并获得访问响应。public class YourHttpHandlerTester : YourHttpHandler
{
public class Response
{
public HttpResponseBase HttpResponse { get; }
public string Body { get; }
public Response(HttpResponseBase httpResponse, string body)
{
HttpResponseBase = httpResponse;
Body = body;
}
}
public Response ProcessRequest(HttpRequestBase httpRequest)
{
var memoryStream = new MemoryStream();
var httpResponse = CreateHttpResponse(memoryStream);
var httpContext = CreateHttpContext(httpRequest, httpResponse);
base.ProcessRequest(httpContext);
var response = CreateResponse(httpResponse, memoryStream);
return response;
}
protected virtual HttpResponseBase CreateHttpResponse(MemoryStream memoryStream)
{
var httpResponseBaseMock = new Moq.Mock<HttpResponseBase>();
httpResponseBaseMock.Setup(x => x.OutputStream).Returns(memoryStream);
return httpResponseBaseMock.Object;
}
protected virtual HttpContextBase CreateHttpContext(HttpRequestBase httpRequest, HttpResponseBase httpResponse)
{
var httpContextBaseMock = new Moq.Mock<HttpContextBase>();
httpContextBaseMock.Setup(x => x.Request).Returns(httpRequest);
httpContextBaseMock.Setup(x => x.Response).Returns(httpResponse);
return httpContextBaseMock.Object;
}
protected virtual Response CreateResponse(HttpResponseBase httpResponse, MemoryStream memoryStream)
{
memoryStream.Position = 0;
var body = new StreamReader(memoryStream).ReadToEnd();
var response = new Response(httpResponse, body);
return response;
}
}
这个类可以快速创建易读的测试:
[Fact]
public void Test()
{
var httpHandler = new YourHttpHandlerTester();
var request = CreateHttpRequestFromString("test");
var response = testHandler.ProcessRequest(request);
Assert.NotEmpty(response.Body);
}
public HttpRequestBase CreateHttpRequestFromString(string body)
{
var httpRequestBaseMock = new Moq.Mock<HttpRequestBase>();
var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(body)))
httpRequestBaseMock.Setup(x => x.InputStream).Returns(stream);
return httpRequestBaseMock.Object;
}
YourHttpHandlerTester
有很多虚方法可以根据需要进行重写。
同样地,Response
类可以进行改进,以便公开来自 HttpResponseBase
的方法,例如 http 状态码、头信息和其他您想要检查的内容。
您可以使用其他答案中提到的方法对处理程序进行集成测试,以进行单元测试,您需要创建一些接口并将处理程序的核心功能提取出来,同时创建一些模拟对象。
您无法对其所有部分进行单元测试,因为它依赖于外部资源(您将进行模拟)-但这没关系,这就是我们进行集成测试的原因。