0
votes

I am writing a test method for an IHttpActionresult controller. The ActionResult is not NULL and contains the required data (Customer.ID = 986574123). However in line two the variable CreatedResult is null. I want it to return the appropriate data to CreatedResult. I am also using Moq framework. Don't know if that matters. Any thoughts? If you need more data from ActionResult please comment below. Thx.

Test Method Code:

        var CustomerRepository = new Mock<ICustomerRepository>();

        CustomerRepository.Setup(x => x.Add()).Returns(new Customer { ID = 986574123, Date = DateTime.Now});      

        var Controller = new CustomerController(CustomerRepository.Object, new Mock<IProductRepository>().Object);
        var config = new HttpConfiguration();
        var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:38306/api/CreateCustomer");
        var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}");
        var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "Customers" } });
        Controller.ControllerContext = new HttpControllerContext(config, routeData, request);
        Controller.Request = request;
        Controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;

        IHttpActionResult ActionResult = Controller.CreateCustomer();
        // Null occurs here
        var CreatedResult = ActionResult as CreatedAtRouteNegotiatedContentResult<Customer>;

CreateCustomer Add method:

         [Route("api/createcustomer")]
         [HttpPost]
         public IHttpActionResult CreateCustomer()
         {
             Customer NewCustomer = CustomerRepository.Add();

             return Created(Request.RequestUri + "/" + NewCustomer.ID.ToString(), new { customerID = NewCustomer.ID });
         }

ActionResult Data:

-       Location    {http://localhost:38306/api/createcustomer/986574123}   System.Uri
        AbsolutePath    "/api/createcustomer/986574123" string
        AbsoluteUri "http://localhost:38306/api/createcustomer/986574123"   string
        Authority   "localhost:38306"   string
        DnsSafeHost "localhost" string
        Fragment    ""  string
        Host    "localhost" string
        HostNameType    Dns System.UriHostNameType
        IsAbsoluteUri   true    bool
        IsDefaultPort   false   bool
        IsFile  false   bool
        IsLoopback  true    bool
        IsUnc   false   bool
        LocalPath   "/api/createCustomer/986574123" string
        OriginalString  "http://localhost:38306/api/createcustomer/986574123"   string
        PathAndQuery    "/api/createCustomer/986574123" string
        Port    38306   int
        Query   ""  string
        Scheme  "http"  string
+       Segments    {string[4]} string[]
        UserEscaped false   bool
        UserInfo    ""  string
1
Since we know the type of CreatedResult is not CreatedAtRouteNegotiatedContentResult<Customer>, what is its type? - Lilshieste
The type is anonymous - user3754602
How would I fix the code while make sure the JSON returned would be the same as following { customerID = NewCustomer.ID }? - user3754602

1 Answers

0
votes

The simplest change to make your test to pass would be change this line

return Created(Request.RequestUri + "/" + NewCustomer.ID.ToString(), new { customerID = NewCustomer.ID });

to the following

return Created(Request.RequestUri + "/" + NewCustomer.ID.ToString(), NewCustomer);

The problem is that that the type parameter of your CreatedAtRouteNegotiatedContentResult is not what you expect. You try to cast the result to a CreatedAtRouteNegotiatedContentResult<Customer> when in fact its type is CreatedAtRouteNegotiatedContentResult<AnonymousType#1>, so the cast fails and returns null.

The reason for this is that ApiController's Create(String, T) method returns a CreatedAtRouteNegotiatedContentResult whose type parameter T is the type of the content you pass in, and you're passing in an anonymous type.


You want to use the anonymous type return only certain fields from your model, but you also want to refer to this type outside of the context where it is declared (i.e. in your unit test). This isn't possible, see the above link about anonymous types (If you must store query results or pass them outside the method boundary, consider using an ordinary named struct or class instead of an anonymous type.)

So, if you want to return just certain fields, you'll need to create a specific view model for this purpose.

class CustomerDetails
{
     public int customerID { get; set; }
}

and then in your action method

return Created(Request.RequestUri + "/" + NewCustomer.ID.ToString(), new CustomerDetails { customerID = NewCustomer.ID });