Насмешливый HttpContextBase с Moq - PullRequest
23 голосов
/ 04 августа 2009

У меня есть прибор модульного тестирования, в котором я пытаюсь протестировать действие ControllerAction на контроллере ASP.NET MVC, который используется для функций членства в веб-приложении. Я пытаюсь издеваться над HttpContext для тестов. Тестируемое действие ControllerAction фактически устанавливает свойства для HttpContext, такие как значения Session, значения 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, который я могу использовать для установки значений для Session и т. Д., Но я не могу понять, как что-то подобное можно смоделировать, не вводя его. Есть ли способ сделать это вместо этого? (Мне также нужно иметь возможность передать контекст моему членству MembershipProvider). Это был бы лучший подход?

Спасибо.

Ответы [ 2 ]

34 голосов
/ 04 августа 2009

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

Так что это может быть легко использовано повторно, создайте класс, подобный тому, который представлен ниже, который вы передадите своему контроллеру. Это позволит настроить ваши макеты и установить их на 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. Для этого я должен поблагодарить Стива Сандерсона - я использую это в качестве основы для тестирования всех видов традиционно трудных для тестирования вещей: файлов cookie, сеанса, метода запроса, строки запроса и многого другого!

23 голосов
/ 04 августа 2009

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

    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, выпущенной Скоттом Хансельманом . Это код Moq 2.0; синтаксис немного отличается в 3.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...