在MVC3中使用Session的静态帮助程序阻止我进行单元测试

时间:2011-05-12 22:23:37

标签: c# asp.net asp.net-mvc-3

我正在c#asp.net mvc3中编写一个项目,我有一个如下所示的辅助类:

using System.Collections.Generic;
using System.Web;

namespace CoPrice.Helpers
{
    public static class Messages
    {
        public static IList<Message> All
        {
            get
            {
                var list = ((IList<Message>)HttpContext.Current.Session["_messages"]) ?? new List<Message>();
                HttpContext.Current.Session["_messages"] = new List<Message>();
                return list;
            }
        }

        public static bool Exists
        {
            get
            {
                return (((IList<Message>)HttpContext.Current.Session["_messages"]) ?? new List<Message>()).Count > 0;
            }
        }

        public static void Add(MessageType type, string message)
        {
            Message m = new Message
            {
                Type = type,
                Text = message
            };
            HttpContext.Current.Session["_messages"] = HttpContext.Current.Session["_messages"] as List<Message> ?? new List<Message>();
            ((IList<Message>)HttpContext.Current.Session["_messages"]).Add(m);
        }

        public enum MessageType
        {
            Info,
            Success,
            Error
        }
        public struct Message
        {
            public MessageType Type;
            public string Text;
        }
    }
}

但是,当我尝试在测试中使用它们时,它会崩溃(导致HttpContext.Current变为null)。如何在测试和应用程序本身中完成这项工作?我不介意必须更改此类以使用除HttpContext.Current之外的其他内容来访问会话,但我希望它具有属性,因此它不能将session-object作为参数。

关于如何解决这个问题的任何想法?

3 个答案:

答案 0 :(得分:2)

您需要定义IMessagesProvider并使用DI容器来注入IMessagesProvider的实现。在实际使用中,您将拥有一个使用ASP.Net会话的实现。在测试使用中,你大多会嘲笑它。顺便说一下,你可能不应该有一个静态的消息类。实际上,IMessagesProvider可能会替换您的静态Messages类。

例如:

public class FooController : Controller
{
    IMessagesProvider _messages;

    public FooController(IMessagesProvider messages)
    {
        // Your DI container will inject the messages provider automatically.
        _messages = messages;
    }

    public ActionResult Index()
    {
        ViewData["messages"] = _messages.GetMessages(Session.SessionId);
        return View();
    }
}

请注意,这是一个非常简单的例子。实际上,您可能还需要一个类,它是驱动视图的模型。快速介绍:

public ActionResult Index()
{
    var model = PrepareModel(_messages);
    return View(model);
}

“PrepareModel”是您自己的方法,它实例化一个新的模型类,用必要的数据填充它,然后将它发送到您的视图。每个视图定义一个模型类通常是典型的。例如。你有像“SignupFormModel”,“DashboardModel”,“ChatModel”等模型类,这样做也可以让你拥有强类型的视图(很棒)。

答案 1 :(得分:0)

您还可以实现从HttpSessionStateBase

继承的模拟会话对象

答案 2 :(得分:0)

@xanadont是正确的,您需要将Messages变成普通类。但是(我知道这在某些圈子里是异端),不需要一个一次性的界面和一个完整的DI框架。只需在Messages类上创建虚拟方法,这样就可以在单元测试中模拟它们,并使用构造函数注入:

public class FooController : Controller
{
    Messages _messages;

    // MVC will call this ctor
    public FooController() : this(new Messages())
    {
    }

    // call this ctor in your unit-tests with a mock object, testing subclass, etc.
    public FooController(Messages messages)
    {
        _messages = messages;
    }    
}