ASP/NET MVC:带会话的测试控制器?嘲讽?

Posted

技术标签:

【中文标题】ASP/NET MVC:带会话的测试控制器?嘲讽?【英文标题】:ASP/NET MVC: Test Controllers w/Sessions? Mocking? 【发布时间】:2010-09-15 15:16:18 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

ASP.NET MVC Tip #12 – Faking the Controller Context

[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"]);

【讨论】:

我不得不挖掘这个网址,所以这里是:stephenwalther.com/blog/archive/2008/07/01/… 这可能不是很明显,但 FakeControllerContext 是一个自定义类。您可以在此处查看其来源:stephenwalther.com/Downloads/Tips/Tip12/Tip12.zip 上面的源链接好像失效了..有人有副本吗? http://web.archive.org/web/*/http://stephenwalther.com/Downloads/Tips/Tip12/Tip12.zip 雅各布,你是男人中的英雄。只要您使用 Controllerbase 而不是 Icontroller,代码仍然可以很好地工作【参考方案2】:

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; 
    

【讨论】:

【参考方案3】:

在 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's post。

【讨论】:

【参考方案4】:

我发现嘲笑是相当容易的。这是一个使用 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   
        

希望对您有所帮助。

【讨论】:

哇,仅仅为了模拟一种方法就需要做很多工作 :) 这显然是“设置过多”的味道,这是使用抽象基类而不是接口作为依赖接缝的结果。 当然,我见过一些项目,他们将设置代码放在“帮助”类中并反复使用。 仅供参考,如果您的测试需要如此多的设置,以至于您需要一个“帮助”类,那么您将会遇到痛苦和摩擦。【参考方案5】:

Scott Hanselman 有一篇关于如何使用 MVC create a file upload quickapp 的帖子并讨论了 moking 并专门解决了“如何模拟不适合模拟的事物”。

【讨论】:

【参考方案6】:

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

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  

【讨论】:

【参考方案7】:

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

【讨论】:

以上是关于ASP/NET MVC:带会话的测试控制器?嘲讽?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 在 ASP.NET Core 3.1 MVC 中使用会话变量

使用 ASP.NET MVC 上传(会话和身份验证)

为啥在单步执行 ASP.NET MVC 控制器时,我的 VS2008 调试会话总是退出?

MVC(ASP.NET MVC)带3层架构如何协同工作?

我在 asp.net MVC 中进行会话处理它在查看页面按钮上工作我无法在我的控制器中的另一个操作上获得会话 [重复]

如何将消息插入取决于会话值的视图。 ASP.NET MVC。最佳实践