如何测试HttpWebRequest依赖方法?

时间:2015-01-30 00:38:39

标签: 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;
        }

    }       

}

并希望单独测试(使用nunit)PostRequest方法。

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

然而,当我尝试测试我的方法时,我收到了这个错误:

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

}

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

谢谢

2 个答案:

答案 0 :(得分:1)

我解决了以下问题:

首先,创建了一个接口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();
    }

在我的测试文件中,我创建了一个&#34; Testable&#34; class,继承自我真正想要测试的类并覆盖虚拟方法:

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

    }
}

答案 1 :(得分:0)

不确定你想要达到的目标,但这可能会有所帮助。错误消息告诉您确切的问题,并提示您该做什么。

如果您检查代码,则类WrapHttpWebRequest的类型不是HttpWebRequest。但是,它持有HttpWebRequest。这两个步骤解决了直接问题,但您可能会遇到另一个问题。首先,为班级WrapHttpWebRequest提供属性:

public HttpWebRequest HttpWebRequest { get { return _request; } }

然后将您失败的代码行更改为:

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

我认为最好更改类WrapHttpWebRequest并继承'HttpWebRequest'这样......

public class WrapHttpWebRequest: HttpWebRequest, IHttpWebRequest

...并相应地更改其实施。