2010-03-13 81 views
20

我有一個實體實現了簡單的添加操作和重定向到詳細頁面控制器:單位在ASP.NET MVC 2測試控制器RedirectToAction

[HttpPost] 
public ActionResult Add(Thing thing) 
{ 
    // ... do validation, db stuff ... 
    return this.RedirectToAction<c => c.Details(thing.Id)); 
} 

這個偉大的工程(使用從RedirectToAction MvcContrib程序集)。

當我單元測試此方法時,我想訪問從Details操作返回的ViewData(這樣我就可以獲取新插入的主鍵並證明它現在在數據庫中)。

測試有:

var result = controller.Add(thing); 

但是導致這裏是類型:System.Web.Mvc.RedirectToRouteResult(這是一個System.Web.Mvc.ActionResult)。它尚未執行Details方法。

我已經試過在返回的對象上調用ExecuteResult傳遞模擬ControllerContext,但是框架並不滿意模擬對象中缺少細節。

我可以嘗試填寫詳細信息等等,但然後我的測試代碼比我測試的代碼長,我覺得我需要爲單元測試進行單元測試!

我在測試理念中錯過了什麼嗎?當我無法恢復到返回狀態時,如何測試此操作?

回答

7

你似乎爲單元測試做得太多了。驗證和數據訪問通常由您從控制器操作調用的服務完成。你嘲笑這些服務,只測試他們被正確調用。

這樣的事情(使用近似的語法Rhino.Mocks & NUnit的):

[Test] 
public void Add_SavesThingToDB() 
{ 
    var dbMock = MockRepository.GenerateMock<DBService>(); 
    dbMock.Expect(x => x.Save(thing)).Repeat.Once(); 

    var controller = new MyController(dbMock); 
    controller.Add(new Thing()); 

    dbMock.VerifyAllExpectations(); 
} 

[Test] 
public void Add_RedirectsAfterSave() 
{ 
    var dbMock = MockRepository.GenerateMock<DBService>(); 

    var controller = new MyController(dbMock); 
    var result = (RedirectToRouteResult)controller.Add(new Thing()); 

    Assert.That(result.Url, Is.EqualTo("/mynew/url")); 
} 
+0

謝謝 - 這絕對有助於避免測試過程中框架的困難。所以,遵循這個習慣用法,我會對DBService進行單元測試,證明我可以添加東西,併爲控制器進行單元測試,以證明其調用服務上的Save。但是我沒有真正證明傳入控制器的東西最終在數據庫中。也許我可以用一堆更復雜的嘲諷規則來做到這一點......但是這並不正確,這是一個簡單操作的很多測試鍋爐板。 – 2010-03-13 23:16:42

+0

那麼,確定測試的範圍和努力以及測試什麼等等,都是我與之奮鬥的事情。我認爲你應該嘗試將你的測試分爲兩類:單元測試和集成測試。單元測試應該只測試非常小的功能單元,比如上面的測試。集成測試應該考慮如何整合所有內容,也許涵蓋您擁有的小用戶故事。我寧願將集成測試儘可能接近「真實」使用,例如運行WatiN並實際單擊一些鏈接。根本不需要嘲笑。 – rmac 2010-03-13 23:43:06

+0

如果你測試你的DBService,那你證明它是有效的。然後,你應該假設它可以並且它會正確處理數據庫調用。所以如果你的控制器使用這個服務,你知道它會起作用。使用模擬框架,您可以驗證傳遞給服務方法的參數,這就足夠了。我認爲rmacfie是對的,你可能試圖做更深入的測試。 你的單元測試應該只包含一個動作,而不是一個完整的過程。 – 2010-03-14 23:53:29

12

有MVC的Contrib TestHelper是夢幻般的測試大部分的ActionResult

您可以在這裏: http://mvccontrib.codeplex.com/wikipage?title=TestHelper

下面是語法的例子:

var controller = new TestController(); 

controller.Add(thing) 
      .AssertActionRedirect() 
      .ToAction<TestController>(x => x.Index()); 

要測試數據是否已成功保存,您應該直接詢問您的數據庫,我不知道您是使用ORM還是其他什麼,但是您應該做一些事情以獲取最後一個被檢測到的項目數據庫,然後比較你提供給你的Add ActionResult的值,看看這是否正常。

我不認爲測試您的Details ActionResult以查看您的數據是否持久是正確的方法。這不會是一個單元測試,更多的是功能測試。

但是,您還應該測試您的Details方法,以確保您的viewdata對象充滿來自數據庫的正確數據。

31

我現在使用的是MVC2 RC2,rmacfie的回答對我來說並不適合,但確實讓我走上了正軌。

不論對錯,我設法做到這一點在我的測試,而不是:

如果
var actionResult = (RedirectToRouteResult)logonController.ForgotUsername(model); 

actionResult.RouteValues["action"].should_be_equal_to("Index"); 
actionResult.RouteValues["controller"].should_be_equal_to("Logon"); 

不知道這會幫助別人,但可能會給您節省10分鐘。

+2

太棒了!您可以用類似的方法檢查其他路線值。對於RedirectToAction(「Details」,「Person」,{personId = 123})''你可以檢查'personId':'Assert.AreEqual(123,actionResult.RouteValues [「personId」])' – Kirill 2012-12-21 05:08:49

6

我有一個測試重定向的靜態幫助器方法。

public static class UnitTestHelpers 
{ 
    public static void ShouldEqual<T>(this T actualValue, T expectedValue) 
    { 
     Assert.AreEqual(expectedValue, actualValue); 
    } 

    public static void ShouldBeRedirectionTo(this ActionResult actionResult, object expectedRouteValues) 
    { 
     RouteValueDictionary actualValues = ((RedirectToRouteResult)actionResult).RouteValues; 
     var expectedValues = new RouteValueDictionary(expectedRouteValues); 

     foreach (string key in expectedValues.Keys) 
     { 
      Assert.AreEqual(expectedValues[key], actualValues[key]); 
     } 
    } 
} 

然後創建重定向測試非常簡單。

[Test] 
public void ResirectionTest() 
{ 
    var result = controller.Action(); 

    result.ShouldBeRedirectionTo(
     new 
     { 
      controller = "ControllerName", 
      action = "Index" 
     } 
    ); 
} 
+3

+1由於某種原因,MVC3控制器名稱爲空 – 2011-11-05 23:14:47