2010-03-23 168 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