Abris
Abris

Reputation: 1481

Unit test custom IHttpactionResult

I'm trying to create a custom IHttpActionResult type in web api 2 that will return content as HTML instead of json. What I'm struggling with is how to unit test an ApiController that returns my new ActionResult type. Many example showing how to unit test an ApiController tells you to cast it to OkNegotiatedContentResult and then read the content property off it but this doesn't seem to work in my case. When I debug the test, the code block in ExecuteAsync never seems to be called. Do I need to do this explicitly in my unit tests? Any help would be much appriciated

This is how my ActionResult looks like

public class HtmlActionResult : IHttpActionResult
{
    String _html;
    public HtmlActionResult(string html)
    {
        _html = html;
    }

    public Task<System.Net.Http.HttpResponseMessage> ExecuteAsync(System.Threading.CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StringContent(_html );
        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/html");
        return Task.FromResult(response);
    }
}

This is my ApiController

public class HomeController : ApiController
{
    public IHttpActionResult Get(string page)
    {
        return new HtmlActionResult("<html></html>");
    }
}

And this is my test method

[TestMethod]
public async Task Get()
{
    //Arrenge
    HomeController controller = new HomeController();

    //Act
    IHttpActionResult result = controller.Get();

    //Assert
    //Assert.IsNotNull(result.Content);
}

Upvotes: 4

Views: 9450

Answers (4)

Matt Stannett
Matt Stannett

Reputation: 2728

Assuming that you're using WebAPI version 2, there is a really good guide on how to Unit Test Controllers on The ASP.NET Site.

I was in a similar scenario to you, and was a bit iffy about making my controller methods return Tasks instead of IHttpActionResults - which I believe are much cleaner.

I managed to adapt the code under the Testing Actions that Return HttpResponseMessage section of the above link to get my unit test working as expected.

Below is a simplified outline of my scenario:

public class XyzController : ApiController
{
    private readonly IDbContext _dbContext;

    public XyzController(IDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    [HttpGet]
    [Route("this-is-optional")]
    public IHttpActionResult Get(<optional-params-here>)
    {
        // Do work
        ...

        // My data is an array of objects
        return Ok(data);
    }
}

[TestFixture]
public class XyzControllerTest
{
    [Test]
    public void Get_ReturnsSuccessfully()
    {
        // Arrange
        IDbContext testContext = MockDbContext.Create();
        ...
        // Populate textContext here
        ...
        XyzController xyzController = new XyzController(testContext)
        {
            // These are required or exceptions will be thrown
            Request = new HttpRequestMessage();
            Configuration = new HttpConfiguration()
        };            
        ...

        // Act
        var response = xyzController.Get(<params-if-required>).ExecuteAsync(CancellationToken.None);

        // Assert
        Assert.IsNotNull(response);
        Assert.IsTrue(response.IsCompleted);
        Assert.AreEqual(HttpStatusCode.OK, response.Result.StatusCode);

        // Assertions on returned data
        MyModel[] models;
        Assert.IsTrue(response.Result.TryGetContentValue<MyModel[]>(out models));
        Assert.AreEqual(5, model.Count());

        Assert.AreEqual(1, model.First().Id);
        ...
    }
}

Upvotes: 2

abatishchev
abatishchev

Reputation: 100238

Use Fluent Assertions:

IHttpActionResult result = await controller.Get()

HtmlActionResult htmlActionResult = result.Should()
                                          .BeOfType<HtmlActionResult>()
                                          .Which; // <-- the key feature
                                          // or .And

what means you can nest assertions:

IHttpActionResult result = await controller.Get()

result.Should().BeOfType<HtmlActionResult>()
      .Which.Content.Should().Be(expected);

Or just as @Spock suggested test things separately:

  • Get() returns IHttpActionResult which is actually HtmlActionResult
  • How HtmlActionResult works independently from Get()

Upvotes: 4

Omar.Alani
Omar.Alani

Reputation: 4130

Try this:

[TestMethod]
public void Get()
{
    //Arrenge
    var controller = new HomeController();

    //Act
    var result = controller.Get().Result as HtmlActionResult;

    //Assert
    Assert.IsNotNull(result);
}

Notice that your test can be void and you don't have to await your Get method, you can use.Result to run the task.

Also I am casting the result to HtmlActionResult, which will end up result to be Null if the result was a different ActionResult like OkResult, or NotFoundResult,...etc.

Hope that helps.

Upvotes: 1

David Keaveny
David Keaveny

Reputation: 4079

You are not waiting for the controller action to complete - you should change your test to something like (this is untried):

public async Task Get()
{
    // Arrange
    HomeController controller = new HomeController();

    // Act
    IHttpActionResult result = await controller.Get();

    // Assert
    Assert.IsNotNull(result.Content);
}

Upvotes: 2

Related Questions