代码之家  ›  专栏  ›  技术社区  ›  Brandon Montgomery

单元测试Web服务-httpContext

  •  14
  • Brandon Montgomery  · 技术社区  · 14 年前

    我想为Web服务编写单元测试。我创建了我的测试项目,引用了我的Web项目(不是服务引用、程序集引用),然后编写了一些代码来测试Web服务——它们工作得很好。但是,有一些服务可以确保用户使用 HttpContext.Current.User.Identity.IsAuthenticated

    在测试环境中,没有httpContext这样的东西,因此测试总是失败。这些类型的Web服务应该如何进行单元测试?

    5 回复  |  直到 11 年前
        1
  •  25
  •   Community CDub    7 年前

    Here 是一个相关的讨论。

    我停止引用 HttpContext.Current 直接。用这个类代替:

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

    和使用 HttpContextFactory.Current 而不是 httpContext.current当前 在我们的代码中。

    然后在测试中写下:

            HttpContextFactory.SetCurrentContext(GetMockedHttpContext());
    

    其中getMockedHttpContext()来自 here 看起来是这样的:

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

    它使用 mocking framework 打电话 moq

    在测试项目中,必须添加对 System.Web System.Web.Abstractions ,在哪里 HttpContextBase 已定义。

        2
  •  2
  •   Aliostad    14 年前

    如果使用模拟,可以将此逻辑包装在另一个类中:

    interface IAuthenticator
    {
       bool IsAuthenticated();
    }
    

    实现真正的目标:

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

    但在测试中,创建一个模拟并返回“真”或“假”:

    Mock<IAuthenticator> mock = new Mock<IAuthenticator>();
    mock.Expect(x => x.IsAuthenticated()).Returns(true);
    
        3
  •  1
  •   Andy S    14 年前

    您可以考虑依赖system.web.abstractions.httpContextBase,而不是使用httpContext.current。System.Web.Abstractions程序集已经为您包装了许多常见的ASP.NET HTTP*类。它们被广泛用于ASP.NET MVC代码。如果您使用的是IOC/DI框架,那么很容易使用。例如,在ninject中:

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

    然后在你的构造器中…

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

    这太简单了,但我希望你能明白…正如Aliostad提到的,您可以使用Rhino Mocks或MoQ等轻松模拟httpContextBase,以测试需要授权的某些操作。

        4
  •  0
  •   Brandon Montgomery    14 年前

    我最后在Web服务上放置了一个属性:

    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
    

    然后在我的Web服务方法中:

    <WebMethod()> _
    Public Function GetProject(ByVal projectId As Int32) As String
    
      If Me.Identity.IsAuthenticated Then
    
        'code here
    
      End If
    
    End Function
    

    然后在我的测试中(我用的是犀牛模型):

    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
  •  0
  •   Dinis Cruz    13 年前

    基于上面的解决方案,我在 O2 Platform 这允许这些模拟类的简单使用,例如,这就是我如何从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();
    

    有关详细信息,请参阅此日志: http://o2platform.wordpress.com/2011/04/05/mocking-httpcontext-httprequest-and-httpresponse-for-unittests-using-moq/