【问题标题】:Unit Testing Web Services - HttpContext单元测试 Web 服务 - HttpContext
【发布时间】:2011-04-30 23:51:33
【问题描述】:

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

在测试的上下文中,没有 HttpContext 这样的东西,所以测试总是失败。应该如何对这些类型的 Web 服务进行单元测试?

【问题讨论】:

    标签: asp.net unit-testing httpcontext


    【解决方案1】:

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

    interface IAuthenticator
    {
       bool IsAuthenticated();
    }
    

    并实现真正的:

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

    但在测试中,创建一个 mock 并返回 true 或 false:

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

    【讨论】:

    • 绝对正确,我认为这实际上应该是一个存根而不是模拟。
    • 你的意思是我们需要一个存根而不是模拟?我不同意,因为无论是否经过身份验证,服务的行为都会有所不同,因此我们需要期望能够返回。我不确定——也不是那么热衷于——存根和模拟的区别,但对我来说,这是一个模拟而不是存根。
    • 如果目的是测试 IAuthenticator 接口是否被正确调用,那么它应该是一个模拟。如果你想测试其他东西,它应该是一个存根。存根永远不会导致测试失败。他们只是为了让事情顺利进行。无论如何,我想这取决于您的模拟框架。在 Rhino Mocks 中,模拟和存根之间存在细微差别:stackoverflow.com/questions/463707/…
    【解决方案2】:

    您可以考虑依赖 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 来测试 SomeOperationNeedingAuthorization。

    【讨论】:

    • 您也可以考虑使用商业模拟工具,例如 Typemock Isolator 或 Telerik 的 JustMock,它可以减轻使用/管理 Http* 抽象的负担。
    【解决方案3】:

    我最终在网络服务上放置了一个属性:

    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
    

    然后在我的网络服务方法中:

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

    然后在我的测试中(我正在使用 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)
    

    【讨论】:

      【解决方案4】:

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

      它使用名为moq的mocking framework

      在您的测试项目中,您必须添加对System.Web 和System.Web.Abstractions 的引用,其中定义了HttpContextBase。

      【讨论】:

      • 我也开始这样做了(用 HttpContextFactory 替换 HttpContext),它确实有助于单元测试(我已经将此功能包装在一个 API 中,您可以在此处查看 o2platform.wordpress.com/2011/04/05/…)
      • 我没有使用最小起订量,所以这并没有让我达到 100%,但它很有帮助。我查看了 Stephen Walther 的假对象寻求帮助:stephenwalther.com/archive/2008/07/01/…
      【解决方案5】:

      基于上面的解决方案,我在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/

      【讨论】:

        猜你喜欢
        • 2011-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-09
        相关资源
        最近更新 更多