如何在单元测试MVC中处理httpcontext

时间:2013-04-18 22:27:41

标签: c# asp.net-mvc unit-testing httpcontext

运行单元测试时出现以下错误: NullReferenceException未被usercode处理。 对象引用未设置为对象的实例。

不确定我哪里出错了。请指教。

我的SessionManager.cs类

public static int customerID
{
    get
    {
        if (HttpContext.Current.Session != null && 
            HttpContext.Current.Session[_customerID] != null)
        {
            return Convert.ToInt32(HttpContext.Current.Session[_customerID]);
        }
        else
        {
            throw new Ramsell.ExceptionManagement.RamsellException();
        }
    }
    set
    {
        if (HttpContext.Current.Session == null) return;
        HttpContext.Current.Session[_customerID] = value;

    }
}

ParticipantController

public ActionResult Index()
{
     int custId = Convert.ToInt32(SessionManager.customerID);
     //code logic about participant lists
     return View();
}

测试方法:

 [TestMethod]
    public void IndexTest()
    {
        ParticipantController pCTarget = new ParticipantController();
        const int page = 1;
        const string sortBy = "FirstName";
        const bool ascending = true;
        const string partname = ""; 
        const int success = -1;
        const string id = "";
        var expected = typeof(ParticipantListViewModel);
        ViewResult result = pCTarget.Index(page, sortBy, ascending, partname, success, id) as ViewResult;
        Assert.AreEqual(expected, result.Model.GetType());

    }

//这是我的模拟课程

 public Mock<RequestContext> RoutingRequestContext { get; private set; }
 public Mock<HttpContextBase> Http { get; private set; }
    public Mock<HttpServerUtilityBase> Server { get; private set; }
    public Mock<HttpResponseBase> Response { get; private set; }
    public Mock<HttpRequestBase> Request { get; private set; }
    public Mock<HttpSessionStateBase> Session { get; private set; }
    public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
    public HttpCookieCollection Cookies { get; private set; }

    public MockContext()
    {
        this.RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
        this.ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
        this.Http = new Mock<HttpContextBase>(MockBehavior.Loose);
        this.Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
        this.Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
        this.Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
        this.Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
        this.Cookies = new HttpCookieCollection();

        this.RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(this.Http.Object);
        this.ActionExecuting.SetupGet(c => c.HttpContext).Returns(this.Http.Object);
        this.Http.SetupGet(c => c.Request).Returns(this.Request.Object);
        this.Http.SetupGet(c => c.Response).Returns(this.Response.Object);
        this.Http.SetupGet(c => c.Server).Returns(this.Server.Object);
        this.Http.SetupGet(c => c.Session).Returns(this.Session.Object);
        this.Request.Setup(c => c.Cookies).Returns(Cookies);

        var sessionContainer = new HttpSessionStateContainer("userID", new SessionStateItemCollection(),
                                                new HttpStaticObjectsCollection(), 1, true,
                                                HttpCookieMode.AutoDetect,
                                                SessionStateMode.InProc, false);

        this.Session.Setup(c => c.Add("AspSession", typeof(HttpSessionState).GetConstructor(
                                    BindingFlags.NonPublic | BindingFlags.Instance,
                                    null, CallingConventions.Standard,
                                    new[] { typeof(HttpSessionStateContainer) },
                                    null)
                            .Invoke(new object[] { sessionContainer })));
    }

1 个答案:

答案 0 :(得分:2)

当您为控制器编写单元测试时,您应该单独测试它 - 即应该模拟控制器的依赖性。因此,请确保您的控制器依赖于抽象而不是具体的SessionManager实现(使您的类非静态并从您的经理类中提取ISessionManager接口):

public interface ISessionManager
{
   int CustomerID { get; set; }
} 

public class HttpSessionManager : ISessionManager
{
    // your current implementation goes here
}

然后模拟这个抽象并将其注入控制器(使用Moq的样本):

[TestMethod]
public void IndexTest()
{
    var sessionManager = new Mock<ISessionManager>(); // Mock dependency
    sessionManager.Setup(m => m.CustomerID).Returns(42);

    const int page = 1;
    const string sortBy = "FirstName";
    const bool ascending = true;
    const string partname = ""; 
    const int success = -1;
    const string id = "";
    var expectedModelType = typeof(ParticipantListViewModel);
    ParticipantController pCTarget = 
         new ParticipantController(sessionManager.Object); // Inject dependency

    var view = pCTarget.Index(page, sortBy, ascending, partname, success, id)
               as ViewResult;

    sessionManager.VerifyGet(m => m.CustomerID); // Verify dependency was called
    Assert.AreEqual(expectedModelType, view.Model.GetType());
}

这是控制器代码:

public class ParticipantController : Controller
{
    private readonly ISessionManager _sessionManager;

    public ParticipantController(ISessionManager sessionManager) 
    {
        _sessionManager = sessionManager;
    }

    public ActionResult Index()
    {
        int custId = _sessionManager.CustomerID;
        //code logic about participant lists
        return View();
    }
} 

请注意,单元测试的目标是确保代码的每一小部分都按预期工作。如果你将依赖于测试中的其他单元,那么测试可能会失败有两个原因 - 你测试的单元表现不如预期,或者依赖项有问题你已经遇到了这种情况 - 控制器测试失败,因为会话管理器抛出异常。测试表明控制器的行为并不像预期的那样。这是真的吗?不 - 它的会话管理器表现不如预期。