Unit Testing Web Services - HttpContext

不问归期 提交于 2019-12-03 02:50:12

问题


I want to write unit tests for a web service. I create my test project, reference my web project (not service reference, assembly reference), then write some code to test the web services - they work fine. However, there are some services which make sure the user is logged in to the web application by using HttpContext.Current.User.Identity.IsAuthenticated.

In the context of the tests, there is no such thing as HttpContext, so the tests always fail. How should these kinds of web services be unit tested?


回答1:


Here is a related discussion.

I stopped referencing HttpContext.Current directly. and use this class instead:

public class HttpContextFactory
{
    private static HttpContextBase m_context;
    public static HttpContextBase Current
    {
        get
        {
            if (m_context != null)
                return m_context;

            if (HttpContext.Current == null)
                throw new InvalidOperationException("HttpContext not available");

            return new HttpContextWrapper(HttpContext.Current);
        }
    }

    public static void SetCurrentContext(HttpContextBase context)
    {
        m_context = context;
    }
}

and use HttpContextFactory.Current instead of HttpContext.Current in our code.

Then you write this in your test:

        HttpContextFactory.SetCurrentContext(GetMockedHttpContext());

where GetMockedHttpContext() is from here and looks like this:

    private System.Web.HttpContextBase GetMockedHttpContext()
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();
        var user = new Mock<IPrincipal>();     
        var identity = new Mock<IIdentity>();

        context.Setup(ctx => ctx.Request).Returns(request.Object);
        context.Setup(ctx => ctx.Response).Returns(response.Object);
        context.Setup(ctx => ctx.Session).Returns(session.Object);
        context.Setup(ctx => ctx.Server).Returns(server.Object);
        context.Setup(ctx => ctx.User).Returns(user.Object);
        user.Setup(x => x.Identity).Returns(identity.Object);
        identity.Setup(id => id.IsAuthenticated).Returns(true);
        identity.Setup(id => id.Name).Returns("test");

        return context.Object;
    }

It uses a mocking framework called moq

In your test project you have to add a reference to System.Web and System.Web.Abstractions, where HttpContextBase is defined.




回答2:


If you are using mocking, you can wrap this logic in another class:

interface IAuthenticator
{
   bool IsAuthenticated();
}

and implement the real one:

class Authenticator : IAuthenticator
{
   bool IsAuthenticated()
   {
      return HttpContext.Current.User.Identity.IsAuthenticated;
   }
}

but in the test, create a mock and return true or false:

Mock<IAuthenticator> mock = new Mock<IAuthenticator>();
mock.Expect(x => x.IsAuthenticated()).Returns(true);



回答3:


You might consider taking a dependency on System.Web.Abstractions.HttpContextBase instead of using HttpContext.Current. The System.Web.Abstractions assembly has a lot of common ASP.NET Http* classes already wrapped for you. They're used all over the ASP.NET MVC code. If you're using an IoC/DI framework, it's pretty easy to use. For example, in Ninject:

Bind<HttpContextBase>.ToMethod(x => new HttpContextWrapper(HttpContext.Current));

and then in your constructor...

public class SomeWebService
{
    private HttpContextBase _httpContext;

    public SomeWebService(HttpContextBase httpContext)
    {
        _httpContext = httpContext;
    }

    public void SomeOperationNeedingAuthorization()
    {
        IIdentity userIdentity = _httpContext.User.Identity;

        if (!userIdentity.IsAuthenticated)
            return;

        // Do something here...
    }
}

That's WAY oversimplified, but I hope you get the idea... As Aliostad mentioned, you can easily mock HttpContextBase using Rhino Mocks or Moq, etc. to test SomeOperationNeedingAuthorization.




回答4:


I ended up putting a property on the web service:

Private mIdentity As System.Security.Principal.IIdentity
Public Property Identity() As System.Security.Principal.IIdentity
  Get
    If mIdentity Is Nothing Then mIdentity = HttpContext.Current.User.Identity
    Return mIdentity
  End Get
  Set(ByVal value As System.Security.Principal.IIdentity)
    mIdentity = value
  End Set
End Property

Then in my web service method:

<WebMethod()> _
Public Function GetProject(ByVal projectId As Int32) As String

  If Me.Identity.IsAuthenticated Then

    'code here

  End If

End Function

Then in my test (I'm using RhinoMocks):

Dim mockery As New MockRepository()
Dim mockIdentity As System.Security.Principal.IIdentity = mockery.DynamicMock(Of System.Security.Principal.IIdentity)()

Dim projectService As New TeamDynamix.Enterprise.Web.Next.ProjectService()
projectService.Identity = mockIdentity
mockIdentity.Stub(Function(i As System.Security.Principal.IIdentity) i.IsAuthenticated).Return(True)



回答5:


Based on the solution above, I've implemented a wrapper class in the O2 Platform that allows the easy use of these mocking classes, for example this is how I can write and read from the HttpRequest.InputStream

var mockHttpContext = new API_Moq_HttpContext();
var httpContext = mockHttpContext.httpContext();
httpContext.request_Write("<html><body>".line());
httpContext.request_Write("   this is a web page".line());  
httpContext.request_Write("</body></html>"); 
return httpContext.request_Read();

see this blog post for more details: http://o2platform.wordpress.com/2011/04/05/mocking-httpcontext-httprequest-and-httpresponse-for-unittests-using-moq/



来源:https://stackoverflow.com/questions/4028056/unit-testing-web-services-httpcontext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!