ASP / NET MVC:testing控制器与会话? 惩戒?
我在这里读了一些答案:testing视图和控制器,并嘲笑,但我仍然无法弄清楚如何testingASP.NET MVC控制器读取和设置会话值(或任何其他基于上下文的variables)。我如何为我的testing方法提供(Session)上下文? 嘲笑答案? 有人有例子吗? 基本上,我想在调用控制器方法之前假冒一个会话,让控制器使用该会话。 有任何想法吗?
检查斯蒂芬·瓦尔特的post上伪造控制器上下文:
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"]); }
ASP.NET MVC框架不是非常模拟友好的(或者说,需要太多的设置才能正确地模拟,并且在testing时引起太多的摩擦,恕我直言),因为它使用了抽象基类而不是接口。 对于每个请求和基于会话的存储,我们都有很好的抽象概念。 我们保持这些抽象非常轻,然后我们的控制器依赖于这些抽象的每个请求或每个会话的存储。
例如,以下是我们如何pipe理表单身份validation的东西。 我们有一个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; } } }
使用MVC RC 1,ControllerContext封装了HttpContext并将其作为属性公开。 这使得嘲笑更容易。 使用Moq模拟一个会话variables,请执行以下操作:
var controller = new HomeController(); var context = MockRepository.GenerateStub<ControllerContext>(); context.Expect(x => x.HttpContext.Session["MyKey"]).Return("MyValue"); controller.ControllerContext = context;
请参阅Scott Gu的post了解更多详情。
我发现嘲笑相当简单。 下面是一个使用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 }
希望有所帮助。
Scott Hanselman写了一篇关于如何使用MVC 创build一个file upload quickapp的文章,并讨论了moking,并特别提到了“如何模拟非模拟友好的东西”。
我使用了下面的解决scheme – 制作一个控制器,让我所有的其他控制器inheritance。
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 { }
因为HttpContext是静态的,所以我使用Typemock Isolator来模拟它,Typemock还有一个为ASP.NETunit testing构build的Add-in自定义,称为Ivonna 。