I am using MS Test for Unit Testing my Web API Controller.
Below is my Web API Controller method with the return type:
public IHttpActionResult MethodName(){ //Code logic
return Ok(new { Data = result, Total = (result.Count() > 0 ? result.First().TotalCount : 0) });
}
In above code snippet:
- Data is of Type IEnumerable<ViewModelName'>
- Total Count is of Type Int
Now for testing above API method return type below is what I have tried :
[TestMethod]
public void TestMethodName(){
var result = controller.MethodName();
//Below line is having issue and thus the contentResult is null
var contentResult = result as OkNegotiatedContentResult<List<ViewModelName>>;
}
Adding Int to OkNegotiatedContentResult throws error as it only excepts single parameter <T'>
Below is the actual return type from the API:
{System.Web.Http.Results.OkNegotiatedContentResult<<>f__AnonymousType0<System.Collections.Generic.List<ViewModel'>, int>>}
How should I convert result so that I can use its content into Assert ?
Thank you in advance. Code sample is appreciated.
CodePudding user response:
The problem you're running into is that your OkNegotiatedContentResult<T>
has an anonymous type for T
. The compiler generates an anonymous type for you when you create an adhoc type as you're doing with new { Date = .., Total =.. }
. By design, anonymous types have a name that you can never use in C# code but that's only valid in the compiled JIT code.
This is why converting your result to OkNegotiatedContentResult<List<ViewModelName>>
won't work because T
is not List<ViewModelName>
but it's of anonymoustype<System.Collections.Generic.List<ViewModel'>, int>>
.
In my opion, the best option is not use an anonymus type but create an actual class that has the two properties you're looking for and use that type both in your controller and in your test.
public class MyData
{
public List<ViewModel> Data { get; set; }
public int Total { get; set; }
}
If you don't want to change the code you could use reflection or the dynamic keyword like this:
dynamic contentResult = (dynamic)result.Content;
var data = contentResult.Data
CodePudding user response:
Here is a solution, which might seem a bit cumbersome at first sight.
So, first make sure that we have received an OkNegotiatedContentResult
//Act
var actionResult = controller.MethodName();
//Assert - OkNegotiated result
var resultType = actionResult.GetType().GetGenericTypeDefinition();
Assert.AreEqual(resultType.GetGenericTypeDefinition(), typeof(OkNegotiatedContentResult<>));
Then make sure that anonymous type defines the Data
property
DISCLAIMER: The below code fails in runtime to retrieve Content
//Assert - Anonymous type
dynamic content = ((dynamic)actionResult).Content;
Assert.IsTrue(content.GetType().GetProperty("Data") != null);
And finally make sure that the Data
property is a collection
//Assert - Data property
Assert.IsInstanceOfType(content.Data, typeof(IEnumerable<ViewModelName>));
var data = (IEnumerable<ViewModelName>)content.Data;