C# 在MVC3中使用会话的静态助手阻止我进行单元测试

C# 在MVC3中使用会话的静态助手阻止我进行单元测试,c#,asp.net,asp.net-mvc-3,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 {

我正在用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;
        }
    }
}
使用System.Collections.Generic;
使用System.Web;
名称空间CoPrice.Helpers
{
公共静态类消息
{
公共静态IList All
{
得到
{
var list=((IList)HttpContext.Current.Session[“_messages”])??new list();
HttpContext.Current.Session[“_messages”]=新列表();
退货清单;
}
}
存在公共静态布尔
{
得到
{
返回(((IList)HttpContext.Current.Session[“_messages”])??新建列表())。计数>0;
}
}
公共静态void Add(MessageType,string message)
{
消息m=新消息
{
类型=类型,
文本=消息
};
HttpContext.Current.Session[“_messages”]=HttpContext.Current.Session[“_messages”]作为列表??新列表();
((IList)HttpContext.Current.Session[“_messages”]).Add(m);
}
公共枚举消息类型
{
信息,
成功,,
错误
}
公共结构消息
{
公共消息类型;
公共字符串文本;
}
}
}
然而,当我尝试在测试中使用这些时,它崩溃了(导致HttpContext.Current being为null)。我如何在测试和应用程序本身中都做到这一点?我不介意必须更改该类以使用HttpContext.Current以外的其他内容来访问会话,但我希望它具有相同的属性,因此它不能将会话对象作为参数


关于如何解决这个问题,您有什么想法吗?

您需要定义一个IMessageProvider,并使用DI容器来注入IMessageProvider的实现。在实际使用中,您将有一个使用ASP.Net会话的实现。在测试使用中,您将主要模拟它。顺便说一句,您可能不应该有一个静态消息类。事实上,IMessagesProvider可能会取代静态消息类

例:

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

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

“PrepareModel”是您自己的方法,它实例化一个新的模型类,用必要的数据填充它,然后将它发送到视图中。通常每个视图定义一个模型类。例如,您可以使用“SignupFormModel”、“DashboardModel”、“ChatModel”等模型类,这样做还可以使用强类型视图(这是一件好事)。

您还可以实现一个从类继承的模拟会话对象。

@xanadont是正确的,您需要将
消息
转换为普通类。但是(我知道这在某些圈子里是异端邪说)不需要一次性的接口和成熟的DI框架。只需将
消息
类上的方法设置为虚拟,以便在单元测试中模拟它们,并使用构造函数注入:

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

这是一个很好的例子,说明了为什么单例是不好的。不过,单例并不总是不好的。这是一个很好的例子,说明了为什么很难测试会话变量。你能提供更多的例子来说明如何做到这一点吗?我一点都没用过。而且,我需要能够从我的视图中使用这个消息提供程序(这就是为什么我将它设置为静态,因为它在我的布局文件中)。这是一个非常大的主题。我将从研究AutoFac和如何让ASP.NETMVC运行开始。通过本教程,您将了解该概念。此外,您的视图不应该知道您的Messages类,这就是控制器的用途。控制器将编排发送到视图的数据。是的,我知道,但这意味着我需要将消息的可用性添加到每个控制器中,因为它由布局视图使用。是的,您确实希望每个控制器中都有消息。。。这就是重点。您可以通过创建自己的控制器基类来帮助您的事业,该基类继承自Controller,并具有您想要的所有公共功能。另一个技巧是,您可以将常见的“视图内容”拉入局部视图或Site.master。。。任何最有意义的都可以。没错,但从长远来看,使用DI容器可以节省更多的时间。当它扩展到几个控制器和多个需要注入的其他依赖项时,手动完成所有配置将很快变得过于繁重(对于两种不同的场景:运行模式和测试模式)。
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;
    }    
}