单元测试 http 处理程序?

发布于 2024-08-31 21:55:42 字数 106 浏览 4 评论 0原文

我当前基于 Asp .net 的项目大量使用 Http 处理程序来处理各种请求?那么,有什么方法可以使用单元测试用例来测试每个处理程序的功能吗?我们使用 Nunit 和 Moq 框架来促进单元测试。

My current project based in Asp .net makes considerable use of Http handlers to process various requests? So, is there any way by which I can test the functionality of each of the handlers using unit test cases? We are using Nunit and Moq framework to facilitate unit testing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

浅唱々樱花落 2024-09-07 21:55:42

我认为不久前的这些博客条目是相关的:

http://www.kongsli.net/nblog/2009/05/03/aspnet-35-improving-testability-with-systemwebabstractions/

http://www.kongsli.net/nblog/2009/05/28/ testability-with-systemwebabstractions-and-no-mock-framework/

有关如何对 HttpHandler 进行单元测试的示例,请参阅第一篇文章中的示例 #2。

I think these blog entries from a while back are relevant:

http://www.kongsli.net/nblog/2009/05/03/aspnet-35-improving-testability-with-systemwebabstractions/

http://www.kongsli.net/nblog/2009/05/28/testability-with-systemwebabstractions-and-no-mock-framework/

See example #2 in the first post for an example on how to unit test an HttpHandler.

第几種人 2024-09-07 21:55:42

如果你不关心单元测试并且想要一些快速而肮脏的东西,你可以使用 Fiddler

如果你想要更多集成方法(单元测试)您可以使用WebRequest 和WebResponse。

If you dont care about unit tests and want something quick and dirty you can use Fiddler

if you want a more integrated approach (Unit testing) you can use the WebRequest and WebResponse.

太阳公公是暖光 2024-09-07 21:55:42

你当然可以,尽管我没有“愤怒”地做过自己。
使用 System.Net.WebClient 对处理程序进行 HTTP 调用,并评估返回的内容,这将允许您测试处理程序的面向公众的接口。

在此示例中,我对目标进行了硬编码,并在 WebClient 上使用将返回字符串的方法。

WebClient 还允许您访问 ResponseHeaders、编码和其他有用的“webby”内容;您也可以上传信息。

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

You sure can, although I haven't done myself "in anger".
Use a System.Net.WebClient to make HTTP calls against your handlers, and evaluate what comes back, that will allow you to test the public facing interface of the handler.

In this example I've hard-coded my target, and I'm using a method on the WebClient that will return a string.

The WebClient also gives you access to the ResponseHeaders, Encoding and other useful 'webby' stuff; you can also upload info as well.

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");
        }
    }
}

You can then use the TestHarness to call the target HttpHandler and verify the results in your tests (or use a better approach to your testing if you know one - I'm not a unit testing guru).

    [TestMethod]
    public void TestMethod1()
    {
        string x = UnitTestHttpHandler.TestHarness.GetString();

        Assert.IsTrue(x.Length > 5);
    }
星星的轨迹 2024-09-07 21:55:42

IHttpHandler 的默认接口不可测试,因为 ProcessRequest(HttpContext context) 的参数不可模拟。

这个答案的灵感来自 这篇文章

为了使您的 IHttpHandler 实现可测试,您必须首先进行一些小更改,以便我们可以使用可模拟的 HttpContextBase

From:

class YourHttpHandler : IHttpHandler
{
    public bool IsReusable => true;

    public void ProcessRequest(HttpContext context)
    {
        /* Your handler implementation */
    }
}

To:

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 上下文)ProcessRequest(HttpContextBase 上下文)

现在可以在测试中使用模拟对象调用 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 状态代码、标头以及您可能想要检查的任何其他内容。

The default interface for IHttpHandler is not testable because the param for ProcessRequest(HttpContext context) is not mockable.

This answer is inspired by this post.

To make your IHttpHandler implementation testable you must first make a small change so we can use the mockable HttpContextBase:

From:

class YourHttpHandler : IHttpHandler
{
    public bool IsReusable => true;

    public void ProcessRequest(HttpContext context)
    {
        /* Your handler implementation */
    }
}

To:

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 */
    }
}

Your functionality needs to be moved from ProcessRequest(HttpContext context) to ProcessRequest(HttpContextBase context).

The ProcessRequest(HttpContextBase context) method can now be called in your tests with a mock object to verify the functionality within.

It's useful the create a helper class that can be used to quickly instantiate YourHttpHandler, send a mocked request, and get access to the response.

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;
    }
}

This class can be used to quickly create readable tests:

[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 has plenty of virtual methods that can be overridden as necessary.

Likewise, the Response class can be improved so that it exposes methods from HttpResponseBase like the http status code, headers, and anything else you may want to check.

染墨丶若流云 2024-09-07 21:55:42

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

您将无法对它的所有部分进行单元测试,因为它依赖于外部资源(您会嘲笑的资源) - 但这很好,这就是我们进行集成测试的原因。

You can do INTEGRATION testing of the handler using the methods mentioned in the other answers, to do UNIT testing you will need to create some interfaces and extract the core functionality out of the handler, as well as create some mock objects.

You won't be able to unit test ALL parts of it because it relies upon outside resources (those you'll be mocking) - but that's fine, thats why we HAVE integration testing.

娇纵 2024-09-07 21:55:42

如果您想测试处理程序和 Web UI 之间的通信,那么集成测试就是您的最佳选择。为了对您的逻辑进行单元测试,您是否不能将业务逻辑分离到其他类中(我会为业务层使用单独的程序集)并在表示层之外模拟/单元测试这些类?

一旦您拥有与表示层分离的结构化(和单元测试)业务层,您的处理程序就可以简单地实例化您的具体内容并调用提供的方法。完成此操作后,您就可以进行集成测试,因为您的业务逻辑将经过单元测试。

If you want to test the communication between your handlers and the Web UI then yes, integration testing is the way to go for that. In order to unit test your logic, could you not instead separate your business logic into other classes (I'd use a separate assembly for the business layer) and mock / unit test these classes instead outside of your presentation layer?

Once you have a structured (and unit tested) business layer that has been separated from the presentation layer your handlers can simply instantiate your concretes and invoke the provided methods. Once this is done, you can then move onto integration testing as your business logic will have been unit tested.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文