Модульное тестирование веб-сервисов - HttpContext - PullRequest
14 голосов
/ 27 октября 2010

Я хочу написать модульные тесты для веб-службы. Я создаю свой тестовый проект, ссылаюсь на свой веб-проект (не на ссылку на сервис, на сборку), затем пишу код для тестирования веб-сервисов - они работают нормально. Однако есть некоторые службы, которые обеспечивают вход пользователя в веб-приложение с помощью HttpContext.Current.User.Identity.IsAuthenticated.

В контексте тестов не существует такого понятия, как HttpContext, поэтому тесты всегда терпят неудачу. Как эти виды веб-сервисов должны тестироваться модульно?

Ответы [ 5 ]

25 голосов
/ 29 октября 2010

Здесь является связанным обсуждением.

Я прекратил ссылаться на 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 () от здесь и выглядит так:

    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

В вашем тестовом проекте вы должны добавить ссылку на System.Web и System.Web.Abstractions, где определено HttpContextBase.

2 голосов
/ 27 октября 2010

Если вы используете насмешку, вы можете заключить эту логику в другой класс:

interface IAuthenticator
{
   bool IsAuthenticated();
}

и реализовать реальную:

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

, но в тесте создайте имитациюи верните true или false:

Mock<IAuthenticator> mock = new Mock<IAuthenticator>();
mock.Expect(x => x.IsAuthenticated()).Returns(true);
1 голос
/ 27 октября 2010

Вы можете рассмотреть возможность использования зависимости от 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...
    }
}

Это очень упрощенно, но я надеюсь, вы поняли идею ... Как упоминал Алиостад,Вы можете легко смоделировать HttpContextBase, используя Rhino Mocks или Moq и т. д., чтобы проверить SomeOperationNeedingAuthorization.

0 голосов
/ 05 апреля 2011

Основываясь на решении, приведенном выше, я реализовал класс-оболочку в 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/

0 голосов
/ 27 октября 2010

В итоге я поместил свойство в веб-сервис:

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