Подтвердить что ты не робот

Издевательская HttpContextBase с Moq

У меня есть инструмент unit test, в котором я пытаюсь проверить ControllerAction на ASP.NET MVC-контроллере, который используется для функций членства в веб-приложении. Я пытаюсь высмеять HttpContext для тестов. Тест ControllerAction на самом деле устанавливает свойства в HttpContext, такие как значения сеанса, значения Response.Cookies и т.д. Это не весь код, но вот грубая выборка теста, которую я пытаюсь запустить

[Test]
public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser()
{
    var context = new Mock<HttpContextBase>() {DefaultValue = DefaultValue.Mock};
    context.SetupAllProperties();
    var provider = new Mock<MembershipProvider>(new object[] {context.Object});
    var controller = new AccountController(context.Object, provider.Object);
    // This just sets up a local FormCollection object with valid user data 
    // in it to use to attempt the registration
    InitializeValidFormData();
    ActionResult result = controller.Register(_registrationData);
    Assert.IsInstanceOfType(typeof(ViewResult), result);
    // Here is where I'd like to attempt to do Assertions against properties 
    // of the HttpContext, like ensuring that a Session object called "User" 
    // exists, and new auth cookie exists on the Response.Cookies collection. 
    // So far I've been unable to successfully check the values of those properties.
    // I've been unsuccessful in getting those properties setup correctly on my 
    // mock object so that my ControllerAction can actually *set* their values, 
    // and that I can make assertions on them afterwards. The above code actually
    // generates a StackOverflowException (which I've reported) on the
    // context.SetupAllProperties() call. What am I doing wrong, or what do I need 
    // to do to be able to set and assert on those context properties?
}

Не уверен, что я делаю неправильно, но мне бы это понравилось, если бы кто-то мог указать мне в правильном направлении и показать мне, как настроить этот макетный объект HttpContextBase, чтобы мой контроллер мог фактически устанавливать значения по его свойствам и Я могу сделать утверждения по этим свойствам, чтобы гарантировать, что мой ControllerAction делает то, что мне нужно.

Я подхожу к этому неправильно? Я знаю, что контроллеры MVC имеют ControllerContext, которые я могу использовать для установки значений для сеанса и т.д., Но я не могу понять, как можно было бы издеваться над чем-то вроде этого, не вставляя его. Есть ли способ сделать это вместо этого? (Мне также нужно иметь возможность передать контекст моему членскому провайдеру тоже). Будет ли это лучший подход?

Спасибо.

4b9b3361

Ответ 1

Я использую версию некоторого кода Стив Сандерсон, включенный в его Pro Asp.NET MVC book... и я в настоящее время имея моральную дилемму, нормально ли размещать здесь код. Как насчет компромисса с сильно урезанной версией?;)

Таким образом, это можно легко использовать повторно, создайте класс, аналогичный приведенному ниже, который вы передадите своему контроллеру. Это создаст ваши макеты и назначит их контроллеру ControllerContext

public class ContextMocks
{
    public Moq.Mock<HttpContextBase> HttpContext { get; set; }
    public Moq.Mock<HttpRequestBase> Request { get; set; }
    public RouteData RouteData { get; set; }

    public ContextMocks(Controller controller)
    {
        //define context objects
        HttpContext = new Moq.Mock<HttpContextBase>();
        HttpContext.Setup(x => x.Request).Returns(Request.Object);
        //you would setup Response, Session, etc similarly with either mocks or fakes

        //apply context to controller
        RequestContext rc = new RequestContext(HttpContext.Object, new RouteData());
        controller.ControllerContext = new ControllerContext(rc, controller);
    }
}

И затем в вашем тестовом методе вы просто создадите экземпляр ContextMocks и передадите объект контроллера, который вы тестируете:

[Test]
Public void test()
{
     var mocks = new ContextMocks(controller);
     var req = controller.Request; 
     //do some asserts on Request object
}

Кажется очень похожим на примеры Крейга, но это с Moq v3. Я должен дать реквизиты Стиву Сандерсону для этого - я использую это как основу для тестирования всех видов традиционно трудно проверяемых вещей: куки, сеанс, метод запроса, запрос и многое другое!

Ответ 2

Вот как я это делаю.

    public static HttpContextBase FakeHttpContext()
    {
        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>();

        request.Expect(req => req.ApplicationPath).Returns("~/");
        request.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/");
        request.Expect(req => req.PathInfo).Returns(string.Empty);
        response.Expect(res => res.ApplyAppPathModifier(It.IsAny<string>()))
            .Returns((string virtualPath) => virtualPath);
        user.Expect(usr => usr.Identity).Returns(identity.Object);
        identity.ExpectGet(ident => ident.IsAuthenticated).Returns(true);

        context.Expect(ctx => ctx.Request).Returns(request.Object);
        context.Expect(ctx => ctx.Response).Returns(response.Object);
        context.Expect(ctx => ctx.Session).Returns(session.Object);
        context.Expect(ctx => ctx.Server).Returns(server.Object);
        context.Expect(ctx => ctx.User).Returns(user.Object);

        return context.Object;
    }

Это расширенная версия библиотеки MvcMockHelpers, выпущенной Scott Hanselman. Это код Moq 2.0; синтаксис несколько отличается в 3.