简体   繁体   English

C#单元测试-无法从IHttpActionResult响应中提取内容

[英]C# Unit tests - Unable to extract Content from the IHttpActionResult responce

I have this controller 我有这个控制器

    [Route("GeocacheAddressObjectList")]
    [HttpPost]
    public async Task<IHttpActionResult> GeocacheAddressObjectList([FromBody] List<GeocacheAddress> addresses)
    {
        //check valid addresses
        if(addresses == null)
        {
            return BadRequest("Invalid addresses. The address list object is null!") as IHttpActionResult;
        }

        ElasticHelper searchHelper = new ElasticHelper(ConfigurationManager.AppSettings["ElasticSearchUri"]);
        List<GeocacheAddress> geocodedAddresses = new List<GeocacheAddress>();

        // check each address in the addresses list against geocache db
        foreach (GeocacheAddress address in addresses)
        {
            var elasticSearchResult = SearchGeocacheIndex(address);

            // found a match
            if (elasticSearchResult.Total != 0)
            {
                SearchProperties standardizedAddressSearch = new SearchProperties();
                standardizedAddressSearch.Size = 1;
                standardizedAddressSearch.From = 0;

                Address elasticSearchResultAddress = elasticSearchResult.Hits.ElementAt(0).Source;

                // query the standardized key in geocache db
                standardizedAddressSearch.ElasticAddressId = elasticSearchResultAddress.Standardized.ToString();

                // the address is already standardized, return the standardized address with its geocode
                if (standardizedAddressSearch.ElasticAddressId == "00000000-0000-0000-0000-000000000000")
                {
                    geocodedAddresses.Add(new GeocacheAddress
                    {
                        Id = address.Id,
                        Street = elasticSearchResultAddress.AddressString,
                        City = elasticSearchResultAddress.City,
                        State = elasticSearchResultAddress.State,
                        ZipCode = elasticSearchResultAddress.Zipcode,
                        Plus4Code = elasticSearchResultAddress.Plus4Code,
                        Country = elasticSearchResultAddress.Country,
                        Latitude = elasticSearchResultAddress.Coordinates.Lat,
                        Longitude = elasticSearchResultAddress.Coordinates.Lon
                    });
                }
                else // perform another query using the standardized key
                {
                    Address standardizedAddress = StandardAddressSearch(standardizedAddressSearch).Hits.ElementAt(0).Source;
                    if (standardizedAddress == null)
                    {
                        return BadRequest("No standardized address found in geocache database") as IHttpActionResult;
                    }

                    geocodedAddresses.Add(new GeocacheAddress()
                    {
                        Id = address.Id,
                        Street = standardizedAddress.AddressString,
                        City = standardizedAddress.City,
                        State = standardizedAddress.State,
                        ZipCode = standardizedAddress.Zipcode,
                        Plus4Code = standardizedAddress.Plus4Code,
                        Country = standardizedAddress.Country,
                        Latitude = standardizedAddress.Coordinates.Lat,
                        Longitude = standardizedAddress.Coordinates.Lon
                    });
                }
            }
            else // not found in geocache db, call SmartStreets API
            {
                List<Address> address_list = new List<Address>();

                using (HttpClient httpClient = new HttpClient())
                {
                    //Send the request and get the response
                    httpClient.BaseAddress = new System.Uri(ConfigurationManager.AppSettings["GeocodingServiceUri"]);
                    httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

                    //Lookup object to perform Geocoding service call
                    var postBody = JsonConvert.SerializeObject(new Lookup()
                    {
                        MaxCandidates = 1,
                        Street = address.Street,
                        City = address.City,
                        State = address.State,
                        ZipCode = address.ZipCode
                    });

                    var requestContent = new StringContent(postBody, Encoding.UTF8, "application/json");

                    // Send the request and get the response
                    var response = await httpClient.PostAsync("GeocodeAddressObject", requestContent);

                    if (!response.IsSuccessStatusCode) //error handling
                    {
                        geocodedAddresses.Add(new GeocacheAddress()
                        {
                            Id = address.Id,
                            Error = response.ReasonPhrase
                        });

                    }

                    Geocode geocodeFromGeocoder = JsonConvert.DeserializeObject<List<Geocode>>(response.Content.ReadAsStringAsync().Result).ElementAt(0);

                    GeocacheAddress geocodedAddress = new GeocacheAddress()
                    {
                        Id = address.Id,
                        Street = geocodeFromGeocoder.CorrectedAddress,
                        City = geocodeFromGeocoder.City,
                        State = geocodeFromGeocoder.State,
                        ZipCode = geocodeFromGeocoder.Zipcode,
                        Plus4Code = geocodeFromGeocoder.Plus4Code,
                        Country = geocodeFromGeocoder.Country,
                        Latitude = geocodeFromGeocoder.Latitude,
                        Longitude = geocodeFromGeocoder.Longitude
                    };

                    geocodedAddresses.Add(geocodedAddress);

                    // check each geocoded address against geocache db
                    Guid standardized_key;

                    var geocodedAddressResult = SearchGeocacheIndex(geocodedAddress);

                    // found a match
                    if (geocodedAddressResult.Total != 0)
                    {
                        Address standardizedAddress = geocodedAddressResult.Hits.ElementAt(0).Source;
                        standardized_key = standardizedAddress.AddressID;
                    }
                    else // not found, insert geocode into geocache db
                    {
                        Address new_standardized_address = createStandardizedAddress(geocodeFromGeocoder);
                        standardized_key = new_standardized_address.AddressID;

                        address_list.Add(new_standardized_address);
                    }

                    // insert non-standardized address into geocache db
                    Address new_nonstandardized_address = createNonStandardizedAddress(address, standardized_key);
                    address_list.Add(new_nonstandardized_address);
                }

                searchHelper.BulkIndex<Address>(address_list, "xxx", "xxx");
            }
        }
        return Json(geocodedAddresses, new Newtonsoft.Json.JsonSerializerSettings()) as IHttpActionResult;
    }

I am writing a unit test to test some part of this controller. 我正在编写一个单元测试来测试此控制器的某些部分。

I want to compare the response received from the controller with the expected value. 我想比较从控制器收到的响应和期望值。 When i debug the result, it shows the content for the response but I am unable to use content like (result.Content) in the code. 当我调试结果时,它显示了响应的内容,但是我无法在代码中使用(result.Content)之类的内容。

When i try to use this line, then it returns null response. 当我尝试使用此行时,则返回空响应。

        var result = await controller.GeocacheAddressObjectList(testGeocacheAddress) as OkNegotiatedContentResult<GeocacheAddress>;

Actual unit test code. 实际的单元测试代码。 I would appreciate any help. 我将不胜感激任何帮助。

  [TestMethod]
    public async Task TestMethod1()
    {
        var controller = new GeocachingController();

        var testGeocacheAddress = new List<GeocacheAddress>();
        testGeocacheAddress.Add(new GeocacheAddress
        {
            City = "Renton",
        });

        var result = await controller.GeocacheAddressObjectList(testGeocacheAddress);

        var expected = GetGeocacheAddress();

        Assert.AreEqual(result.Content.City, expected[0].City);

} }

private List<GeocacheAddress> GetGeocacheAddress()
    {
        var testGeocacheAddress = new List<GeocacheAddress>();
        testGeocacheAddress.Add(new GeocacheAddress
        {
            Id = Guid.Empty,
            Street = "365 Renton Center Way SW",
            City = "Renton",
            State = "WA",
            ZipCode = "98057",
            Plus4Code = "2324",
            Country = "USA",
            Latitude = 47.47753,
            Longitude = -122.21851,
            Error = null
        });

        return testGeocacheAddress;
    }

In your unit test you need to cast the result to JsonResult<T> , more specifically JsonResult<List<GeocacheAddress>> as that is what you are returning. 在单元测试中,您需要将结果转换为JsonResult<T> ,更具体地说是JsonResult<List<GeocacheAddress>>因为这就是您要返回的内容。

var result = await controller.GeocacheAddressObjectList(testGeocacheAddress) as JsonResult<List<GeocacheAddress>>;

If you were to have used return Ok(geocodedAddresses) in your controller return ( where you now return the call from Json ) then you could have cast to OkNegotiatedContentResult<List<GeocacheAddress>> . 如果要在控制器return( 现在从Json返回调用 return Ok(geocodedAddresses)中使用return Ok(geocodedAddresses) OkNegotiatedContentResult<List<GeocacheAddress>> ),则可以转换为OkNegotiatedContentResult<List<GeocacheAddress>>


Also in your Controller code you do not need to cast the return to IHttpActionResult because JsonResult<T> already implements that. 同样在您的Controller代码中,您不需要将返回值IHttpActionResult JsonResult<T>IHttpActionResult因为JsonResult<T>已经实现了它。 The cast is redundant. 演员表是多余的。

That's quite simple to achieve, all you have to do is cast the content you're expecting in your unit test method. 这很容易实现,您所要做的就是在单元测试方法中转换您期望的内容。

Example: 例:

Controller: 控制器:

public class FooController : ApiController
    {
        public IHttpActionResult Get()
        {
            var foo = "foo";
            return Ok(foo);
        }
    }

Unit test: 单元测试:

[TestMethod]
    public void Get_Foo_From_Controller()
    {
        var fooController = new FooController();
        var result = fooController.Get();
        //Here we are casting the expected type
        var values = (OkNegotiatedContentResult<string>)result;
        Assert.AreEqual("Foo", values.Content);
    }

By the way, i noticed you're using the async keyword in your controller action but i don't see the await keyword. 顺便说一句,我注意到您在控制器操作中使用了async关键字,但是我没有看到await关键字。

Using the async keyword without an await will give you a warning and result in a synchronous operation. 在不等待的情况下使用async关键字将向您发出警告,并导致同步操作。

Also, you dont have to cast your response as an IHttpActionResult, you could do something like i showed in my example, wrap your content inside an Ok(your content here) and you're good to go. 另外,您不必将响应强制转换为IHttpActionResult,可以执行如我在示例中所示的操作,将您的内容包装在Ok(您的内容在此处)中,就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM