单元测试HTTP处理程序?

16

我的当前基于Asp.net的项目在处理各种请求时大量使用Http处理程序。那么,是否有任何方法可以使用单元测试用例测试每个处理程序的功能?我们正在使用Nunit和Moq框架来实现单元测试。

6个回答

3

链接已失效 =( - Felipe Santos
这是两个与编程有关的网址: https://web.archive.org/web/20091226121202/https://www.kongsli.net/nblog/2009/05/03/aspnet-35-improving-testability-with-systemwebabstractions/ 和 https://web.archive.org/web/20110724004730/https://www.kongsli.net/nblog/2009/05/28/testability-with-systemwebabstractions-and-no-mock-framework/ - Cristian Diaconescu

2

如果您不关心单元测试,想要快速而简单的解决方案,可以使用Fiddler

如果您希望采用更加集成的方法(单元测试),可以使用WebRequest和WebResponse。


1

当然可以,虽然我自己还没有“愤怒地”这样做过。

使用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);
    }

1
但是,我们需要将请求(使用配置文件)路由到正确的处理程序,服务器也需要运行,对吧?那么如何模拟呢? - MockedMan.Object
是的,需要运行Web服务器。至于配置方面,我没有经验,但我认为它应该类似于在单元测试中通过配置指定其他设置。很抱歉我不能提供更多信息。 - Adrian K
这实际上运行得相当不错,唯一的问题是你当然无法通过目标处理程序进行调试。 - Brent

1

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 状态码、头信息和其他您想要检查的内容。


0

您可以使用其他答案中提到的方法对处理程序进行集成测试,以进行单元测试,您需要创建一些接口并将处理程序的核心功能提取出来,同时创建一些模拟对象。

您无法对其所有部分进行单元测试,因为它依赖于外部资源(您将进行模拟)-但这没关系,这就是我们进行集成测试的原因。


0
如果您想测试处理程序和Web UI之间的通信,那么集成测试是实现这一目标的方法。为了对逻辑进行单元测试,您可以将业务逻辑分离到其他类中(我会使用单独的程序集来处理业务层),并在演示层之外模拟/单元测试这些类,而不是直接进行单元测试。
一旦您有了一个结构化(并经过单元测试)的业务层,该层已从演示层中分离出来,您的处理程序只需实例化您的具体类并调用提供的方法即可。完成此操作后,您可以继续进行集成测试,因为您的业务逻辑已经通过单元测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接