c# - 如何测试 HttpWebRequest 依赖的方法?

标签 c# unit-testing httpwebrequest nunit moq

我有这门课:

public class RestClient
{
    public RestClient() 
    { }

    protected virtual HttpWebRequest CreateHttpWebRequest(Uri uri)
    {
        return (HttpWebRequest)HttpWebRequest.Create(uri);
    }


    /// <summary>
    /// Perform a http POST request in order to push data to server
    /// </summary>
    /// <param name="uri">End Point Uri</param>
    /// <param name="data">Data to be transmitted</param>
    /// <returns></returns>
    /// 
    public long PostRequest(Uri uri,string data) 
    {

        try
        {
            HttpWebRequest request = CreateHttpWebRequest(uri); //(HttpWebRequest)HttpWebRequest.Create(uri);

            request.Method = "POST";
            request.ContentType = "application/json";

            System.Text.UTF8Encoding encoding = new UTF8Encoding();

            byte[] bytes = encoding.GetBytes(data);



            using (Stream requestStream = request.GetRequestStream())
            {
                //Transmit data
                requestStream.Write(bytes, 0, bytes.Length);
                requestStream.Flush();
                requestStream.Close();
            }



            //Get the Response from the server
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.NoContent)
                {
                    throw new Exception(String.Format(
                       "Server error (HTTP {0}: {1}).",
                            response.StatusCode,
                                response.StatusDescription));
                }
            }


            return request.ContentLength;
        }
        catch (Exception e)
        {
            throw e;
        }

    }       

}

并希望对 PostRequest 方法进行单元测试(使用 nunit)。

做一些研究,我可以在这篇文章 ( Is it possible to mock out a .NET HttpWebResponse? ) 中找到一些模拟 HttpWebRequest 的方法,并在这篇文章 ( How to unit test a method with HttpWebRequest/Response dependencies ) 中找到一种将它注入(inject)类的方法。

但是,当我尝试测试我的方法时,出现了这个错误:

System.InvalidCastException : Unable to cast object of type 'Castle.Proxies.IHttpWebRequestProxy' to type 'System.Net.HttpWebRequest'.

在我的测试中

client.HttpWebRequestFake = (HttpWebRequest)factory.Object.Create("http://127.0.0.1");

这是我的测试代码:

public class TesableRestClient : RestClient
{
    public HttpWebRequest HttpWebRequestFake { get; set; }

    protected override HttpWebRequest CreateHttpWebRequest(Uri url)
    {
        if (HttpWebRequestFake != null)
            return HttpWebRequestFake;
        return base.CreateHttpWebRequest(url);
    }
}


[TestFixture]
public class TransferWebRequestTest
{
    [Test]
    public void TestPostResquest()
    {
        string expectedContent  = "Content";
        var expectedBytes = Encoding.UTF8.GetBytes(expectedContent);
        var responseStream = new MemoryStream();
        responseStream.Write(expectedBytes, 0, expectedBytes.Length);
        responseStream.Seek(0, SeekOrigin.Begin);

        var response = new Mock<IHttpWebResponse>();
        response.Setup(c => c.GetResponseStream()).Returns(responseStream);

        var request = new Mock<IHttpWebRequest>();
        request.Setup(c => c.GetResponse()).Returns(response.Object);

        var factory = new Mock<IHttpWebRequestFactory>();
        factory.Setup(c => c.Create(It.IsAny<string>()))
            .Returns(request.Object);

        TesableRestClient client = new TesableRestClient();
        client.HttpWebRequestFake = (HttpWebRequest)factory.Object.Create("http://127.0.0.1");

        // DoStuff call the url with a request and then processes the
        long bytesSent = client.PostRequest(new Uri("http://127.0.0.1"), expectedContent);
         Assert.AreEqual(expectedBytes, bytesSent);
}

HttpWebRequest/Response 是这样的:

public interface IHttpWebRequest
{
    // expose the members you need
    string Method { get; set; }
    string ContentType { get; set; }
    long ContentLength { get; set; }
    IHttpWebResponse GetResponse();
}

public interface IHttpWebResponse : IDisposable
{
    // expose the members you need
    HttpStatusCode StatusCode { get; }
    string StatusDescription { get;}
    Stream GetResponseStream();
}

public interface IHttpWebRequestFactory
{
    IHttpWebRequest Create(string uri);
}

// barebones implementation

public class HttpWebRequestFactory : IHttpWebRequestFactory
{
    public IHttpWebRequest Create(string uri)
    {
        return new WrapHttpWebRequest((HttpWebRequest)WebRequest.Create(uri));
    }
}

public class WrapHttpWebRequest : IHttpWebRequest
{
    private readonly HttpWebRequest _request;

    public WrapHttpWebRequest(HttpWebRequest request)
    {
        _request = request;
    }

    public string Method
    {
        get { return _request.Method; }
        set { _request.Method = value; }
    }

    public string ContentType
    {
        get { return _request.ContentType; }
        set { _request.ContentType = value; }
    }

    public long ContentLength
    {
        get { return _request.ContentLength; }
        set { _request.ContentLength = value; }
    }

    public IHttpWebResponse GetResponse()
    {
        return new WrapHttpWebResponse((HttpWebResponse)_request.GetResponse());
    }
}

public class WrapHttpWebResponse : IHttpWebResponse
{
    private HttpWebResponse _response;

    public WrapHttpWebResponse(HttpWebResponse response)
    {
        _response = response;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_response != null)
            {
                ((IDisposable)_response).Dispose();
                _response = null;
            }
        }
    }

    public Stream GetResponseStream()
    {
        return _response.GetResponseStream();
    }

    public HttpStatusCode StatusCode
    {
        get { return _response.StatusCode; }
    }

    public string StatusDescription
    {
        get { return _response.StatusDescription; }
    }

}

知道如何解决这个问题吗?

谢谢

最佳答案

我通过以下方式解决了我的问题:

首先,创建了一个接口(interface)IHttpWebRequestFactory

public interface IHttpWebRequestFactory
{
    HttpWebRequest Create(string uri);
}

在我要测试的类中,我创建了以下方法:

protected virtual HttpWebRequest CreateHttpWebRequest(Uri uri)
    {
        return (HttpWebRequest)HttpWebRequest.Create(uri);
    }

    protected virtual HttpWebResponse GetHttpWebResponse(HttpWebRequest request)
    {
        return (HttpWebResponse)request.GetResponse();
    }

在我的测试文件中,我创建了一个“Testable”类,它继承 self 真正想要测试的类并覆盖了虚拟方法:

 //Class Created to test the PostRequestMethod
public class TestableRestClient : RestClient
{
    public HttpWebRequest HttpWebRequestFake { get; set; }

    public string responseValue;

    protected override HttpWebRequest CreateHttpWebRequest(Uri url)
    {
        if (HttpWebRequestFake != null)
            return HttpWebRequestFake;
        return base.CreateHttpWebRequest(url);
    }

    protected override HttpWebResponse GetHttpWebResponse(HttpWebRequest request)
    {
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        using (var streamReader = new StreamReader(request.GetResponse().GetResponseStream()))
        {
            responseValue = streamReader.ReadToEnd();
        }
        return base.GetHttpWebResponse(request);
    }
}

然后我使用 Moq 来模拟我在类里面使用的方法的行为

 [TestFixture]
public class DMSTransferWebRequestTest
{
    [Test]
    public void TestPostResquest()
    {      

        string expected = "Content";

        //Prepare the Mocked Response Stream
        byte [] expectedBytes = Encoding.UTF8.GetBytes(expected);
        Stream responseStream = new MemoryStream();
        responseStream.Write(expectedBytes, 0, expectedBytes.Length);
        responseStream.Seek(0, SeekOrigin.Begin);

        //Prepare the Mocked Request Stream
        Stream requestStream = new MemoryStream();
        requestStream.Write(expectedBytes, 0, expectedBytes.Length);
        requestStream.Seek(0, SeekOrigin.Begin);

        //Mock the HttpWebResponse
        Mock<HttpWebResponse> response = new Mock<HttpWebResponse>();

        //Set the method GetResponseStream to return the Response Stream mocked
        response.Setup(c => c.GetResponseStream()).Returns(responseStream);
        response.Setup(c => c.StatusCode).Returns(HttpStatusCode.OK);

        //Set the method GetRequestStream to return the Request Stream mocked
        Mock<HttpWebRequest> request = new Mock<HttpWebRequest>();
        request.Setup(c => c.GetResponse()).Returns(response.Object);
        request.Setup(c => c.GetRequestStream()).Returns(requestStream);

        //Create a Object to mock the HttpWebRequest Create Method
        Mock<IHttpWebRequestFactory> factory = new Mock<IHttpWebRequestFactory>();
        factory.Setup(c => c.Create(It.IsAny<string>()))
            .Returns(request.Object);


        TestableRestClient client = new TestableRestClient();
        client.HttpWebRequestFake = factory.Object.Create("http://mytest");

        long actualBytes = client.PostRequest(new Uri("http://mytest"), expected);

        string actual = client.responseValue;


        Assert.AreEqual(expected, actual);

    }
}

关于c# - 如何测试 HttpWebRequest 依赖的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28227357/

相关文章:

.net - 使用 HTTPWebrequest 上传文件(multipart/form-data)

C# WinForms AnimateWindow 问题

c# - foreach 更喜欢 IEnumerable<T> 还是 IEnumerator<T>

javascript - 如何在单元测试中触发 FileReader 的 `onloadend`?

unit-testing - Angular2 单元测试 : testing a component's constructor

c# - HttpWebRequest 非常慢!

.net - 通过身份验证自动从网站下载图片

c# - Unity - 如何从父游戏对象检测子对象的碰撞?

c# - WCF 自托管通过 Https 失败

unit-testing - 模拟Kotlin类不可模拟