开发者

Unit Testing Web Services - HttpContext

开发者 https://www.devze.com 2023-01-22 06:32 出处:网络
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 te开发者_如何学运维st the web serv

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 te开发者_如何学运维st 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?


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.


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


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.


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)


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/

0

精彩评论

暂无评论...
验证码 换一张
取 消