4

I have written this controller method and this test.

Controller method:

public async Task<IActionResult> Metric(string type, string source)
{
    // Check existence ...

    var model = await _context
        .Metrics
        .FirstAsync(mt => mt.Type == metricType.AsInt() && mt.Source == source);

    Response.StatusCode = HttpStatusCode.OK.AsInt();

    return View(model);
}

Test:

[Fact]
public async Task MetricExistsTest()
{
    // Arrange ...

    // Act
    var result = await _controller.Metric(Metrics.CpuLoad.ToString(), "source-1");

    // Assert
    var viewResult = Assert.IsType<ViewResult>(result);

    Assert.Equal(HttpStatusCode.OK.AsInt(), viewResult.StatusCode.Value);

    var model = Assert.IsAssignableFrom<Metric>(
        viewResult.ViewData.Model
    );
}

Now, the problem is here Assert.Equal(HttpStatusCode.OK.AsInt(), viewResult.StatusCode.Value);. The viewResult.StatusCode is indeed null. If I comment that line out, everything works.

What am I doing wrong? Why is it null? Do I properly set Response.StatusCode? How do I verify status code then?

Thank you!

Muhammed Shevil KP
  • 1,404
  • 1
  • 16
  • 21
Dmytro Bogatov
  • 776
  • 1
  • 10
  • 23

2 Answers2

3

I finally did it! All those who helped me with answers and comments - I very much appreciate it!

It turns out that I had two problems - HttpContext does not exist (unless set manually) in testing environment and Response.StatusCode does not set StatusCode on resulting ViewResult object. (These are my observations, correct me if I'm wrong).

Problem 1 solution:

As simple as setting default HttpContext solves the problem. At least, controller method does not crash because Response is not null anymore.

var controller = new HomeController();

controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();

Problem 2 solution:

It turns out that I need to set StatusCode explicitly on ViewResult object. For some reason, ASP.Core does not mirror StatusCode from Response object to resulting IActionObject. (Correct me if I'm wrong)

So here is the solution (it's another method on my controller, but it clearly demonstrates the idea):

public async Task<IActionResult> Index()
{
    var model = await _context
        .Metrics
        .Where(mt => mt.Type == Metrics.CpuLoad.AsInt())
        .ToListAsync();

    var result = View(model);
    result.StatusCode = (model.Any() ? HttpStatusCode.OK : HttpStatusCode.NoContent).AsInt();

    return result;
}
Dmytro Bogatov
  • 776
  • 1
  • 10
  • 23
  • 1
    Note that a `204` response should not have a body/view, though. https://httpstatuses.com/204: "A 204 response is terminated by the first empty line after the header fields because it cannot contain a message body." – hangy Jun 20 '17 at 11:44
  • @hangy Very true. Don't get surprised when your browser refuses to load the content of 204 page, or even does not add the URL to history. – Dmytro Bogatov Jun 20 '17 at 17:03
0

It looks like you should be Asserting the result, not the viewresult

[Fact]
public async Task MetricExistsTest()
{
    // Arrange ...

    // Act
    var result = await _controller.Metric(Metrics.CpuLoad.ToString(), "source-1");

    // Assert
    var viewResult = Assert.IsType<ViewResult>(result);

    Assert.Equal(HttpStatusCode.OK.AsInt(), result.StatusCode.Value);

    var model = Assert.IsAssignableFrom<Metric>(
        viewResult.ViewData.Model
    );
}
Steven Ackley
  • 593
  • 7
  • 31