首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在c#中对HTTP请求进行单元测试

在c#中对HTTP请求进行单元测试
EN

Stack Overflow用户
提问于 2012-02-01 20:14:43
回答 4查看 65K关注 0票数 33

我正在编写一些调用web服务、读回响应并对其执行某些操作的代码。我的代码名义上是这样的:

代码语言:javascript
复制
string body = CreateHttpBody(regularExpression, strategy);

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);
request.Method = "POST";
request.ContentType = "text/plain; charset=utf-8";

using (Stream requestStream = request.GetRequestStream())
{
    requestStream.Write(Encoding.UTF8.GetBytes(body), 0, body.Length);
    requestStream.Flush();
}

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    byte[] data = new byte[response.ContentLength];

    using (Stream stream = response.GetResponseStream())
    {
        int bytesRead = 0;

        while (bytesRead < data.Length)
        {
            bytesRead += stream.Read(data, bytesRead, data.Length - bytesRead);
        }
    }

    return ExtractResponse(Encoding.UTF8.GetString(data));
}

我实际执行自定义操作的唯一部分是在ExtractResponseCreateHttpBody方法中。然而,只对这些方法进行单元测试感觉是错误的,并希望其余的代码能够正确地组合在一起。有没有办法截取HTTP请求并提供模拟数据给它?

EDIT此信息现已过期。使用System.Net.Http.HttpClient库构建这类代码要容易得多。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-02-01 20:24:32

在您的代码中,您不能拦截对HttpWebRequest的调用,因为您在相同的方法中创建了对象。如果您让另一个对象创建HttpWebRequest,则可以传入一个模拟对象并使用该对象进行测试。

因此,不是这样:

代码语言:javascript
复制
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);

使用以下命令:

代码语言:javascript
复制
IHttpWebRequest request = this.WebRequestFactory.Create(_url);

在您的单元测试中,您可以传入一个创建模拟对象的WebRequestFactory

此外,您可以在单独的函数中拆分流读取代码:

代码语言:javascript
复制
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    byte[] data = ReadStream(response.GetResponseStream());
    return ExtractResponse(Encoding.UTF8.GetString(data));
}

这使得单独测试ReadStream()成为可能。

要执行更多的集成测试,您可以设置自己的HTTP服务器来返回测试数据,并将该服务器的URL传递给您的方法。

票数 22
EN

Stack Overflow用户

发布于 2016-01-19 08:47:49

如果模拟HttpWebRequest和HttpWebResponse变得太麻烦,或者如果你需要在验收测试中测试代码,你从“外部”调用你的代码,那么创建一个假的服务可能是最好的方法。

我实际上编写了一个名为MockHttpServer的开源库来帮助实现这一点,这使得模拟任何通过HTTP进行通信的外部服务变得非常简单。

下面是一个使用它和RestSharp一起调用API端点的示例,但是HttpWebRequest也可以很好地工作。

代码语言:javascript
复制
using (new MockServer(3333, "/api/customer", (req, rsp, prm) => "Result Body"))
{
    var client = new RestClient("http://localhost:3333/");
    var result = client.Execute(new RestRequest("/api/customer", Method.GET));
}

在GitHub页面上有一个相当详细的自述文件,详细介绍了使用它的所有选项,并且可以通过NuGet获得该库本身。

票数 9
EN

Stack Overflow用户

发布于 2012-02-01 20:29:52

我可能会从重构代码开始,以使其更弱地耦合到实际的HTTP请求。现在,这段代码似乎可以做很多事情。

这可以通过引入抽象来实现:

代码语言:javascript
复制
public interface IDataRetriever
{
    public byte[] RetrieveData(byte[] request);
}

现在,您尝试进行单元测试的类可以使用控制反转设计模式从实际的HTTP请求中解耦:

代码语言:javascript
复制
public class ClassToTest
{
    private readonly IDataRetriever _dataRetriever;
    public Foo(IDataRetriever dataRetriever)
    {
        _dataRetriever = dataRetriever;
    }

    public string MethodToTest(string regularExpression, string strategy)
    {
        string body = CreateHttpBody(regularExpression, strategy);
        byte[] result = _dataRetriever.RetrieveData(Encoding.UTF8.GetBytes(body));
        return ExtractResponse(Encoding.UTF8.GetString(result));
    }
}

ClassToTest不再负责处理实际的HTTP请求。它现在是解耦的。测试MethodToTest成为一项微不足道的任务。

很明显,最后一部分是我们所介绍的抽象的实现:

代码语言:javascript
复制
public class MyDataRetriever : IDataRetriever
{
    private readonly string _url;
    public MyDataRetriever(string url)
    {
        _url = url;
    }

    public byte[] RetrieveData(byte[] request)
    {
        using (var client = new WebClient())
        {
            client.Headers[HttpRequestHeader.ContentType] = "text/plain; charset=utf-8";
            return client.UploadData(_url, request);
        }
    }
}

然后,您可以配置您喜爱的DI框架,将一个MyDataRetriever实例注入到实际应用程序中的ClassToTest类构造函数中。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9095627

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档