如何在c#中对HTTP请求进行单元测试?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (36)

我正在编写一些调用Web服务的代码,回读响应。我的代码看起来像这样:

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

有什么办法可以拦截HTTP请求并反馈给模拟数据吗?

提问于
用户回答回答于

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

所以不是这样的:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);

用这个:

IHttpWebRequest request = this.WebRequestFactory.Create(_url);

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

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

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    byte[] data = ReadStream(response.GetResponseStream());
    return ExtractResponse(Encoding.UTF8.GetString(data));
}

这使得可以单独测试ReadStream()。

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

用户回答回答于

我可能会从重构代码,以使其更加弱耦合到实际的HTTP请求。

这可以通过引入抽象来完成:

public interface IDataRetriever
{
    public byte[] RetrieveData(byte[] request);
}

现在,尝试单元测试的类可以使用Inversion of Control设计模式从实际的HTTP请求中分离出来:

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请求。它现在是分离的。

实现抽象:

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类构造函数中。

扫码关注云+社区