Wes Doyle
Wes Doyle

Reputation: 2287

Unit Testing IHttpActionResult without casting return type

I have a web API controller with a method like (truncated non-relevant bits):

[HttpGet]
public IHttpActionResult Order(int orderId) {
    // ...
    var response = new OrderResponse {
        Data = orderData,
        Message = message,
    };
    return Json(response);
}

I have a test that makes an assertion about the value of the expected JsonResult<OrderResponse>.Content.

Since the interface IHttpActionResult does not declare Content, I am currently casting the result in the unit test to a JsonResult. However, this requires my test to know how the return is implemented in my controller, and I'd like to achieve looser coupling, if possible, so that my tests don't break if the response implementation changes.

[TestCase(-1, "The requested order (-1) was not found.")]
public void Test_Returns_NotFound_For_NonExistent_OrderId(int orderId, string expected) {
    var sut = new OrderController()
    // ...
    var result = sut.Order(orderId);

    // I am doing this to get access to `Content`:
    var materializedResult = result as JsonResult<OrderResponse>;

    expected.Should().BeEquivalentTo(materializedResult.Content.Message)
}

Upvotes: 3

Views: 1272

Answers (2)

Scott Hannen
Scott Hannen

Reputation: 29207

Instead of returning Json(response), return Ok(response). You don't need to specify that it is formatted as JSON. If the client requests JSON, the application will return JSON. (That's assuming you're using default content negotiation.)

Then in a unit test you can retrieve your data from the returned IHttpActionResult like this:

var actionResult = sut.Order(orderId);
var orderResponse = actionResult.ExecuteAsync(new CancellationToken())
    .Result.TryGetContentValue(out OrderResponse expected);

...and you can assert whatever you want with expected, and your test doesn't make any assumption about how the response is formatted.

Upvotes: 2

bvoyelr
bvoyelr

Reputation: 980

In short? Only use the methods that are available on the interface!

  var controller = new ValuesController();
  controller.Configuration = new System.Web.Http.HttpConfiguration();
  controller.Request = new System.Net.Http.HttpRequestMessage();
  var result = controller.Get(1);

  var response = result.ExecuteAsync(new System.Threading.CancellationToken());
  String deserialized = response.Result.Content.ReadAsStringAsync().Result;
  Assert.AreEqual("\"value\"", deserialized);

IHttpActionResult only contains one method -- ExecuteAsync. So call it! It'll return an HttpResponseMessage which will contain the Content you're looking for. You'll probably want to make your unit tests async and actually await the result, but this should work to get you started.

Upvotes: 3

Related Questions