Переменная IHttpActionResult, передающая значение null в методе тестирования

#c# #unit-testing #moq #asp.net-web-api2 #actionmethod

#c# #модульное тестирование #moq #asp.net-web-api2 #actionmethod

Вопрос:

Я пишу метод тестирования для контроллера IHttpActionResult. Результат ActionResult не равен нулю и содержит требуемые данные (Customer.ID = 986574123). Однако во второй строке переменная CreatedResult имеет значение null. Я хочу, чтобы она возвращала соответствующие данные в CreatedResult. Я также использую фреймворк Moq. Не знаю, имеет ли это значение. Есть мысли? Если вам нужны дополнительные данные из ActionResult, пожалуйста, прокомментируйте ниже. Спасибо.

Код метода тестирования:

         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:

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

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

Данные ActionResult:

 -       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. Поскольку мы знаем, что тип CreatedResult не CreatedAtRouteNegotiatedContentResult<Customer> является, каков его тип?

2. Тип анонимный

3. Ну, тогда это проблема. Анонимные типы не могут быть преобразованы в любой другой тип (кроме Object ) .

4. Как бы я исправил код, убедившись, что возвращаемый JSON будет таким же, как следующий { CustomerID = NewCustomer . ID }?

Ответ №1:

Самым простым изменением для прохождения вашего теста будет изменение этой строки

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

для следующего

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

Проблема в том, что параметр типа вашего CreatedAtRouteNegotiatedContentResult — это не то, что вы ожидаете. Вы пытаетесь привести result к a CreatedAtRouteNegotiatedContentResult<Customer> , когда на самом деле его тип равен CreatedAtRouteNegotiatedContentResult<AnonymousType#1> , поэтому приведение завершается ошибкой и возвращается null .

Причина этого в том, что Create(String, T) метод ApiController возвращает CreatedAtRouteNegotiatedContentResult, параметром типа T которого является тип content передаваемого вами, и вы передаете анонимный тип.


Вы хотите использовать анонимный тип, возвращающий только определенные поля из вашей модели, но вы также хотите ссылаться на этот тип вне контекста, в котором он объявлен (т. Е. В вашем модульном тестировании). Это невозможно, см. Ссылку выше об анонимных типах ( 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. )

Итак, если вы хотите вернуть только определенные поля, вам нужно будет создать для этой цели конкретную модель представления.

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

а затем в вашем методе действий

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

Комментарии:

1. Хорошо, я добавил больше информации. Какие условия должны быть выполнены?

2. @user3754602 Я обновил свой ответ в свете предоставленной вами новой информации.

3. Спасибо. Есть ли какой-либо способ вернуть что-то вроде этого «new { CustomerID = NewCustomer. ID }»? Я не хочу отправлять обратно клиенту все данные клиента, и я хочу, чтобы переменная JSON называлась CustomerID

4. Есть ли способ передать AnonymousType клиенту? Затем передать его через CreatedAtRouteNegotiatedContentResult?

5. @user3754602 вы не можете приводить анонимные типы ни к чему, кроме Object (в документации MSDN это указано). Смотрите мой ответ для другого возможного решения.