使用Moq在NUnit测试中模拟HttpContext.Current

3

我正在测试一个调用此类方法的MVC 3控制器:

public class SessionVar
{
    /// <summary>
    /// Gets the session.
    /// </summary>
    private static HttpSessionState Session
    {
        get
        {
            if (HttpContext.Current == null)
                throw new ApplicationException
                                   ("No Http Context, No Session to Get!");

            return HttpContext.Current.Session;
        }
    }

    public static T Get<T>(string key)
    {
        return Session[key] == null ? default(T) : (T)Session[key];
    }
    ...
}

我遵循Hanselman的博客的建议,我的测试方法如下:

[Test]
public void CanRenderEmployeeList()
{
    _mockIEmployeeService.Setup(s => s.GetEmployees(StatusFilter.OnlyActive))
        .Returns(BuildsEmployeeList().Where(e => e.IsApproved));

    var httpContext = FakeHttpContext();
    var target = _employeeController;
    target.ControllerContext = new ControllerContext
                  (new RequestContext(httpContext, new RouteData()), target);
    var result = target.Index();

    Assert.IsNotNull(result);
    Assert.IsInstanceOf<ViewResult>(result);
    var viewModel = target.ViewData.Model;
    Assert.IsInstanceOf<EmployeeListViewModel>(viewModel);
}

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>();

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

    return context.Object;
}

但是我的测试一直失败,我得到了以下结果:
CanRenderEmployeeListSystem.ApplicationException : No Http Context, 
                                                          No Session to Get!

HttpContext.Current == null时需要抛出的异常信息是什么?

我只需要“存在”Session对象,而不需要Session中实际存储的值。

你能告诉我哪里做错了吗?

谢谢。

2个回答

2
您的Session属性中的HttpContext.Current并没有受到您创建的模拟HttpContextBase的影响。也就是说,仅仅创建一个本地HttpContextBase并不会自动填充HttpContext.Current。实际上,HttpContext和HttpContextBase没有实际关联。您需要使用HttpContextWrapper来统一它们。
因此,最好将HttpContextWrapper实现传递到您的类SessionVar中。在下面的代码中,我已经将您的方法和属性更改为实例级别,以便我们可以在构造函数中设置上下文。如果没有将上下文传递给构造函数,我们假设HttpContext.Current,但您也可以在测试时传递模拟实例。
public class SessionVar
{
    HttpContextWrapper m_httpContext;

    public SessionVar(HttpContextWrapper httpContext = null)
    {
        m_httpContext = httpContext ?? new HttpContextWrapper(HttpContext.Current);
    }

    /// <summary>
    /// Gets the session.
    /// </summary>
    private HttpSessionState Session
    {
        get
        {
            if (m_httpContext == null)
                throw new ApplicationException("No Http Context, No Session to Get!");

            return m_httpContext.Session;
        }
    }

    public T Get<T>(string key)
    {
        return Session[key] == null ? default(T) : (T)Session[key];
    }
    ...
}

我认为这也是一个不错的解决方案,但我更喜欢@PatrickSteele的建议,实际上我最终采用了他的建议。感谢您的帮助。 - qbantek

2

从长远来看,如果为SessionVar类创建一个接口,您会更加快乐。在运行时使用当前的依赖注入实现,而在测试期间插入模拟对象。这将消除所有Http运行时依赖项的模拟需求。


这就是我最终做的事情,不知怎么的感觉像是一个变通方法...但后来我发现其他人也是用同样的方式做的,在你在这里的评论之后:这种变通的感觉几乎消失了 :) - qbantek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接