2010-03-23 165 views
28

我想为我的一个控制器编写单元测试,以验证视图是否正确返回,但此控制器有访问HttpContext.Current.Session的基本控制器。每次创建控制器的新实例时,都调用basecontroller构造函数,并且测试失败,HttpContext.Current.Session上出现空指针异常。下面是代码:ASP.NET MVC单元测试控制器与HttpContext

public class BaseController : Controller 
{  
    protected BaseController() 
    { 
     ViewData["UserID"] = HttpContext.Current.Session["UserID"]; 
    } 
} 

public class IndexController : BaseController 
{ 
    public ActionResult Index() 
    { 
     return View("Index.aspx"); 
    } 
} 

    [TestMethod] 
    public void Retrieve_IndexTest() 
    { 
     // Arrange 
     const string expectedViewName = "Index"; 

     IndexController controller = new IndexController(); 

     // Act 
     var result = controller.Index() as ViewResult; 

     // Assert 
     Assert.IsNotNull(result, "Should have returned a ViewResult"); 
     Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName); 
    } 

如何嘲笑(使用MOQ)是在基本控制器访问,从而在后代控制器的测试将要运行的会话的任何想法?

回答

5

如果您正在使用Typemock,你可以这样做:

Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"]) 
.WillReturn("your id"); 

测试代码如下:

[TestMethod] 
public void Retrieve_IndexTest() 
{ 
    // Arrange 
    const string expectedViewName = "Index"; 

    IndexController controller = new IndexController(); 
    Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"]) 
    .WillReturn("your id"); 
    // Act 
    var result = controller.Index() as ViewResult; 

    // Assert 
    Assert.IsNotNull(result, "Should have returned a ViewResult"); 
    Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName); 
} 
3

你应该使用ActionFilter而不是基类为这种事

[UserIdBind] 
public class IndexController : Controller 
{ 
    public ActionResult Index() 
    { 
     return View("Index.aspx"); 
    } 
} 
+0

如果我使用动作过滤器的方法,我将不得不装饰与此属性,并与ISN约400行动的每一个动作不可行 – amurra 2010-03-23 11:41:57

+0

@ user299592:不,你不会的。您可以在课堂级应用它(如示例所示),并将其应用于该课程中的每个动作。如果你认为这将比为每个动作的每个测试嘲笑一个上下文(很少或没有实际使用你在构造函数中设置的字段)要花费更多的工作量,这足够公平。 – pdr 2010-03-23 12:17:40

+0

您的正确我没有在课堂上看到它,但是在实例化控制器对象时仍不会调用动作过滤器吗? – amurra 2010-03-24 02:10:55

61

除非您使用Typemock或Moles,you can't

在ASP.NET MVC中,你不应该使用HttpContext.Current。更改您的基类以使用ControllerBase.ControllerContext - 它具有HttpContext属性,该属性公开可测试的HttpContextBase类。

这里是你如何使用最小起订量来建立一个模拟HttpContextBase一个例子:

var httpCtxStub = new Mock<HttpContextBase>(); 

var controllerCtx = new ControllerContext(); 
controllerCtx.HttpContext = httpCtxStub.Object; 

sut.ControllerContext = controllerCtx; 

// Exercise and verify the sut 

其中sut代表被测系统(SUT),即你希望控制器来测试。

+0

你能指点我如何嘲笑莫尔斯这个方向吗? – BritishDeveloper 2010-07-01 10:53:56

+0

绝对要走的路;出于这个确切原因,微软添加了HttpContextBase。现在如果只有Cache和FormsAuthentication不是密封/静态的... – 2012-03-19 00:12:41

3

段:

var request = new SimpleWorkerRequest("/dummy", @"c:\inetpub\wwwroot\dummy", "dummy.html", null, new StringWriter()); 
var context = new HttpContext(request); 
SessionStateUtility.AddHttpSessionStateToContext(context, new TestSession()); 
HttpContext.Current = context; 

TestSession()的执行情况:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Web.SessionState; 

namespace m1k4.Framework.Test 
{ 
    public class TestSession : IHttpSessionState 
    { 
     private Dictionary<string, object> state = new Dictionary<string, object>(); 

     #region IHttpSessionState Members 

     public void Abandon() 
     { 
      throw new NotImplementedException(); 
     } 

     public void Add(string name, object value) 
     { 
      this.state.Add(name, value); 
     } 

     public void Clear() 
     { 
      throw new NotImplementedException(); 
     } 

     public int CodePage 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Web.HttpCookieMode CookieMode 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public void CopyTo(Array array, int index) 
     { 
      throw new NotImplementedException(); 
     } 

     public int Count 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Collections.IEnumerator GetEnumerator() 
     { 
      throw new NotImplementedException(); 
     } 

     public bool IsCookieless 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsNewSession 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsReadOnly 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsSynchronized 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Collections.Specialized.NameObjectCollectionBase.KeysCollection Keys 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public int LCID 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public SessionStateMode Mode 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public void Remove(string name) 
     { 
      this.state.Remove(name); 
     } 

     public void RemoveAll() 
     { 
      this.state = new Dictionary<string, object>(); 
     } 

     public void RemoveAt(int index) 
     { 
      throw new NotImplementedException(); 
     } 

     public string SessionID 
     { 
      get 
      { 
       return "Test Session"; 
      } 
     } 

     public System.Web.HttpStaticObjectsCollection StaticObjects 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object SyncRoot 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public int Timeout 
     { 
      get 
      { 
       return 10; 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object this[int index] 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object this[string name] 
     { 
      get 
      { 
       return this.state[name]; 
      } 
      set 
      { 
       this.state[name] = value; 
      } 
     } 

     #endregion 
    } 
} 
+1

如果您使用Rhino.Mocks,您可以简单地执行此操作'SessionStateUtility.AddHttpSessionStateToContext(httpContext,MockRepository.GenerateStub ());'而不是自己实现TestSession。 – 2011-03-09 10:56:57