43

我在这里阅读了一些答案:测试视图和控制器以及模拟,但我仍然无法弄清楚如何测试读取和设置会话值(或任何其他基于上下文的变量)的 ASP.NET MVC 控制器。如何为我的测试方法提供(会话)上下文?是在嘲讽答案吗?有人有例子吗?基本上,我想在调用控制器方法并让控制器使用该会话之前伪造一个会话。有任何想法吗?

4

7 回答 7

44

查看 Stephen Walther 关于伪造控制器上下文的帖子:

ASP.NET MVC 技巧 #12 – 伪造控制器上下文

[TestMethod]
public void TestSessionState()
{
    // Create controller
    var controller = new HomeController();


    // Create fake Controller Context
    var sessionItems = new SessionStateItemCollection();
    sessionItems["item1"] = "wow!";
    controller.ControllerContext = new FakeControllerContext(controller, sessionItems);
    var result = controller.TestSession() as ViewResult;


    // Assert
    Assert.AreEqual("wow!", result.ViewData["item1"]);

    // Assert
    Assert.AreEqual("cool!", controller.HttpContext.Session["item2"]);
}
于 2008-10-26T18:03:30.387 回答
14

由于使用抽象基类而不是接口,ASP.NET MVC 框架对模拟不是很友好(或者更确切地说,需要太多设置才能正确模拟,并且在测试时会导致太多摩擦,恕我直言)。我们很幸运地为每个请求和基于会话的存储编写了抽象。我们将这些抽象保持得很轻,然后我们的控制器依赖于这些抽象来进行每个请求或每个会话的存储。

例如,这是我们管理表单身份验证内容的方式。我们有一个 ISecurityContext:

public interface ISecurityContext
{
    bool IsAuthenticated { get; }
    IIdentity CurrentIdentity { get; }
    IPrincipal CurrentUser { get; set; }
}

有一个具体的实现,如:

public class SecurityContext : ISecurityContext
{
    private readonly HttpContext _context;

    public SecurityContext()
    {
        _context = HttpContext.Current;
    }

    public bool IsAuthenticated
    {
        get { return _context.Request.IsAuthenticated; }
    }

    public IIdentity CurrentIdentity
    {
        get { return _context.User.Identity; }
    }

    public IPrincipal CurrentUser
    {
        get { return _context.User; }
        set { _context.User = value; }
    }
}
于 2008-10-06T22:02:43.617 回答
10

在 MVC RC 1 中,ControllerContext 包装了 HttpContext 并将其作为属性公开。这使得模拟更容易。要使用 Moq 模拟会话变量,请执行以下操作:

var controller = new HomeController();
var context = MockRepository.GenerateStub<ControllerContext>();
context.Expect(x => x.HttpContext.Session["MyKey"]).Return("MyValue");
controller.ControllerContext = context;

有关详细信息,请参阅Scott Gu 的帖子

于 2009-02-17T18:00:56.237 回答
5

我发现嘲笑是相当容易的。这是一个使用 moq 模拟 httpContextbase(包含请求、会话和响应对象)的示例。

[TestMethod]
        public void HowTo_CheckSession_With_TennisApp() {
            var request = new Mock<HttpRequestBase>();
            request.Expect(r => r.HttpMethod).Returns("GET");     

            var httpContext = new Mock<HttpContextBase>();
            var session = new Mock<HttpSessionStateBase>();

            httpContext.Expect(c => c.Request).Returns(request.Object);
            httpContext.Expect(c => c.Session).Returns(session.Object);

            session.Expect(c => c.Add("test", "something here"));            

            var playerController = new NewPlayerSignupController();
            memberController.ControllerContext = new ControllerContext(new RequestContext(httpContext.Object, new RouteData()), playerController);          

            session.VerifyAll(); // function is trying to add the desired item to the session in the constructor
            //TODO: Add Assertions   
        }

希望有帮助。

于 2008-10-06T22:59:48.573 回答
2

Scott Hanselman 有一篇关于如何使用 MVC创建文件上传快速应用的帖子,并讨论了 moking 并专门解决了“如何模拟不适合 mock 的东西”。

于 2008-10-06T22:22:14.683 回答
2

我使用了以下解决方案 - 制作一个我所有其他控制器都继承自的控制器。

public class TestableController : Controller
{

    public new HttpSessionStateBase Session
    {
        get
        {
            if (session == null)
            {
                session = base.Session ?? new CustomSession();
            }
            return session;
        }
    }
    private HttpSessionStateBase session;

    public class CustomSession : HttpSessionStateBase
    {

        private readonly Dictionary<string, object> dictionary; 

        public CustomSession()
        {
            dictionary = new Dictionary<string, object>();
        }

        public override object this[string name]
        {
            get
            {
                if (dictionary.ContainsKey(name))
                {
                    return dictionary[name];
                } else
                {
                    return null;
                }
            }
            set
            {
                if (!dictionary.ContainsKey(name))
                {
                    dictionary.Add(name, value);
                }
                else
                {
                    dictionary[name] = value;
                }
            }
        }

        //TODO: implement other methods here as needed to forefil the needs of the Session object. the above implementation was fine for my needs.

    }

}

然后使用如下代码:

public class MyController : TestableController { }
于 2012-03-25T13:31:15.157 回答
0

因为 HttpContext 是静态的,所以我使用 Typemock Isolator 来模拟它,Typemock 还有一个为ASP.NET 单元测试定制的 Add-in,称为Ivonna

于 2009-04-24T09:01:13.973 回答