Reputation: 1481
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
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
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:
Upvotes: 4
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
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