2011-02-14 70 views
39

如果我有一個這樣的控制器:如何對返回JsonResult的Action方法進行單元測試?

[HttpPost] 
public JsonResult FindStuff(string query) 
{ 
    var results = _repo.GetStuff(query); 
    var jsonResult = results.Select(x => new 
    { 
     id = x.Id, 
     name = x.Foo, 
     type = x.Bar 
    }).ToList(); 

    return Json(jsonResult); 
} 

基本上,我搶東西從我的資料庫,然後投射成匿名類型的List<T>

我該如何進行單元測試?

System.Web.Mvc.JsonResult有一個名爲Data的屬性,但它的類型爲object,正如我們所預期的那樣。那麼這是否意味着如果我想測試JSON對象具有我期望的屬性(「id」,「name」,「type」),我必須使用反射嗎?

編輯:

這裏是我的測試:

// Arrange. 
const string autoCompleteQuery = "soho"; 

// Act. 
var actionResult = _controller.FindLocations(autoCompleteQuery); 

// Assert. 
Assert.IsNotNull(actionResult, "No ActionResult returned from action method."); 
dynamic jsonCollection = actionResult.Data; 
foreach (dynamic json in jsonCollection) 
{ 
    Assert.IsNotNull(json.id, 
     "JSON record does not contain \"id\" required property."); 
    Assert.IsNotNull(json.name, 
     "JSON record does not contain \"name\" required property."); 
    Assert.IsNotNull(json.type, 
     "JSON record does not contain \"type\" required property."); 
} 

但我得到一個運行時錯誤的循環,指出「對象不包含一個定義ID」。

當我斷點時,actionResult.Data被定義爲一個匿名類型的List<T>,所以我想如果我通過這些枚舉,我可以檢查屬性。在循環內部,對象確實有有一個叫做「id」的屬性 - 所以不知道問題是什麼。

+0

重新編輯 - 你可以嘗試像var items =(IEnumerable)actionResult.Data; foreach(動態obj中的項目){...} – 2011-02-14 22:57:05

+1

我已經在這裏測試了' var list =(IList)數據; Assert.AreEqual(list.Count,2); dynamic obj = data [0]; Assert.AreEqual(obj.id,12); Assert.AreEqual(obj.name,「Fred」); Assert.AreEqual(obj.type,'a'); obj = data [1]; Assert.AreEqual(obj.id,14); Assert.AreEqual(obj.name,「Jim」); Assert.AreEqual(obj.type,'c'); foreach(動態列表中的d) if(d.id == null)throw new InvalidOperationException(); }`看起來很好...... – 2011-02-15 06:11:16

回答

14

RPM,你看起來是正確的。我還有很多需要了解dynamic,我也無法讓Marc的方法工作。所以這就是我以前的做法。您可能會發現它很有幫助。我只寫了一個簡單的擴展方法:

public static object GetReflectedProperty(this object obj, string propertyName) 
    { 
     obj.ThrowIfNull("obj"); 
     propertyName.ThrowIfNull("propertyName"); 

     PropertyInfo property = obj.GetType().GetProperty(propertyName); 

     if (property == null) 
     { 
      return null; 
     } 

     return property.GetValue(obj, null); 
    } 

然後,我只是用它來作我的JSON數據斷言:

 JsonResult result = controller.MyAction(...); 
        ... 
     Assert.That(result.Data, Is.Not.Null, "There should be some data for the JsonResult"); 
     Assert.That(result.Data.GetReflectedProperty("page"), Is.EqualTo(page)); 
49

我知道我是一個有點晚於這個傢伙,但我發現爲什麼動態解決方案不起作用:

JsonResult返回一個匿名對象,這些對象默認爲internal,所以它們需要讓測試項目可見。

打開您的ASP.NET MVC應用程序項目,並從名爲Properties的文件夾中找到AssemblyInfo.cs。打開AssemblyInfo.cs並將以下行添加到此文件的末尾。

[assembly: InternalsVisibleTo("MyProject.Tests")] 

引自:http://weblogs.asp.net/gunnarpeipman/archive/2010/07/24/asp-net-mvc-using-dynamic-type-to-test-controller-actions-returning-jsonresult.aspx

我認爲這將是不錯的這個備案。工程就像一個魅力

1

我的解決方案,從馬特·格里爾延伸,並拿出這個小擴展:

public static JsonResult IsJson(this ActionResult result) 
    { 
     Assert.IsInstanceOf<JsonResult>(result); 
     return (JsonResult) result; 
    } 

    public static JsonResult WithModel(this JsonResult result, object model) 
    { 
     var props = model.GetType().GetProperties(); 
     foreach (var prop in props) 
     { 
      var mv = model.GetReflectedProperty(prop.Name); 
      var expected = result.Data.GetReflectedProperty(prop.Name); 
      Assert.AreEqual(expected, mv); 
     } 
     return result; 
    } 

,我只是運行單元測試是這樣的: - 將預期數據結果:

 var expected = new 
     { 
      Success = false, 
      Message = "Name is required" 
     }; 

- 斷言結果:

​​
1

這裏有一個I U se,也許這對任何人都有用。它測試返回JSON對象以用於客戶端功能的操作。它使用Moq和FluentAssertions。

[TestMethod] 
public void GetActivationcode_Should_Return_JSON_With_Filled_Model() 
{ 
    // Arrange... 
    ActivatiecodeController activatiecodeController = this.ActivatiecodeControllerFactory(); 
    CodeModel model = new CodeModel { Activation = "XYZZY", Lifespan = 10000 }; 
    this.deviceActivatieModelBuilder.Setup(x => x.GenereerNieuweActivatiecode()).Returns(model); 

    // Act... 
    var result = activatiecodeController.GetActivationcode() as JsonResult; 

    // Assert... 
    ((CodeModel)result.Data).Activation.Should().Be("XYZZY"); 
    ((CodeModel)result.Data).Lifespan.Should().Be(10000); 
} 
1

我的解決方法是編寫擴展方法:

using System.Reflection; 
using System.Web.Mvc; 

namespace Tests.Extensions 
{ 
    public static class JsonExtensions 
    { 
     public static object GetPropertyValue(this JsonResult json, string propertyName) 
     { 
      return json.Data.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public).GetValue(json.Data, null); 
     } 
    } 
} 
5

我有點遲到了,但我創建了一個小包裝,讓我,然後用dynamic性能。至於這個答案,我已經在ASP.NET Core 1.0 RC2上工作了,但我相信如果你用resultObject.Data代替resultObject.Value它應該適用於非核心版本。

public class JsonResultDynamicWrapper : DynamicObject 
{ 
    private readonly object _resultObject; 

    public JsonResultDynamicWrapper([NotNull] JsonResult resultObject) 
    { 
     if (resultObject == null) throw new ArgumentNullException(nameof(resultObject)); 
     _resultObject = resultObject.Value; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     if (string.IsNullOrEmpty(binder.Name)) 
     { 
      result = null; 
      return false; 
     } 

     PropertyInfo property = _resultObject.GetType().GetProperty(binder.Name); 

     if (property == null) 
     { 
      result = null; 
      return false; 
     } 

     result = property.GetValue(_resultObject, null); 
     return true; 
    } 
} 

用法,假設下面的控制器:

public class FooController : Controller 
{ 
    public IActionResult Get() 
    { 
     return Json(new {Bar = "Bar", Baz = "Baz"}); 
    } 
} 

測試(的xUnit):

// Arrange 
var controller = new FoosController(); 

// Act 
var result = await controller.Get(); 

// Assert 
var resultObject = Assert.IsType<JsonResult>(result); 
dynamic resultData = new JsonResultDynamicWrapper(resultObject); 
Assert.Equal("Bar", resultData.Bar); 
Assert.Equal("Baz", resultData.Baz); 
0

如果在測試你確切地知道JSON數據結果應該是什麼,那麼你可以這樣做:

result.Data.ToString().Should().Be(new { param = value}.ToString()); 

P.S.這將是如果你使用FluentAssertions.Mvc5 - 但它不應該很難轉換爲你使用的任何測試工具。

相關問題