2013-06-01 93 views
25

我試圖在我的控制器之一中返回一個JsonResult單元測試一個方法。令我驚訝的下面的代碼沒有工作:斷言包含匿名類型的JsonResult

[HttpPost] 
public JsonResult Test() { 
    return Json(new {Id = 123}); 
} 

這是我如何測試它(也注意到,測試代碼駐留在另一個組件):

// Act 
dynamic jsonResult = testController.Test().Data; 

// Assert 
Assert.AreEqual(123, jsonResult.Id); 

Assert拋出異常:

'對象' 不包含一個定義爲 'ID'

我已經解決它通過使用以下內容:

[HttpPost] 
public JsonResult Test() { 
    dynamic data = new ExpandoObject(); 
    data.Id = 123; 
    return Json(data); 
} 

我想了解爲什麼不是第一個工作?它似乎也在基本上處理任何事情,但一個匿名類型。

+1

我試過你的代碼與匿名類型,它對我工作得很好。不知道爲什麼你得到這個錯誤。 –

+0

打印出'jsonResult.GetType()'時會得到什麼? (錯誤表明它認爲它的類型是'object',而不是類型'<> f__AnonymousType0',這是我所期望的) –

+0

該類型確實是對象。我預計它會自己工作,不知道爲什麼我會得到這些結果。 –

回答

29

要清楚,您遇到的具體問題是C#動態不適用於非公共成員。這是設計的,大概是爲了勸阻這種事情。正如LukLed所述,匿名類型只在同一個程序集內公開(或者更確切地說,匿名類型只是標記爲internal而不是public),所以你正在遇到這個障礙。

也許最乾淨的解決方案將是你使用InternalsVisibleTo。它允許您命名另一個可以訪問其非公開成員的程序集。使用它進行測試是其存在的主要原因之一。

[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")] 

一旦你這樣做,錯誤就會消失(我只是嘗試過自己):在你的榜樣,你會在你的主項目的AssemblyInfo.cs以下行的地方。

或者,你可以只用蠻力反思:

Assert.AreEqual(123, jsonResult.GetType().GetProperty("Id").GetValue(jsonResult, null)); 
+0

你能解釋最後一個選項嗎? – Ani

+0

你能澄清你的問題嗎?你是否想知道一般的思考或關於它如何適用於這個問題/答案? –

+0

反思一般 – Ani

4

匿名類型是內部的,所以你不能將它們暴露給另一個庫,那是一個有測試的庫。如果您將測試代碼放置在與控制器相同的庫中,則它將起作用。

+0

我不知道,非常感謝。我很感謝你的回答。 –

+1

您也可以將測試程序集聲明爲「朋友程序集」以公開內部。 http://msdn.microsoft.com/en-us/library/0tke9fxk(v=vs.80).aspx –

10

已經在這裏,然後尋找更遠的地方閱讀的回答,我發現了一個2009 msdn blog post用不同的方法試。但是..在評論中是一個非常簡單和非常優雅的解決方案Kieran ...使用.ToString()

在原來的情況:

[HttpPost] 
public JsonResult Test() 
{ 
    return Json(new {Id = 123}); 
} 

你可以做測試:

var jsonResult = controller.Test(); 
Assert.AreEqual("{Id = 123}", jsonResult.Data.ToString()); 

,我更喜歡這種解決方案,因爲它:

  • 避免改變原有的代碼(InternalsVisibleToExpandoObject),
  • 避免使用MvcContribRhinoMocks(與兩者但爲什麼添加只是爲了能夠測試JsonResult?沒有問題),並且,
  • 避免使用反射(增加了複雜性的測試)。