问题
In an ASP.NET Core 1.0 (MVC 6) project I have a Controller action method in which I use the Response
property to set a header:
[HttpGet]
public IActionResult Get()
{
...
Response.Headers.Add("Location", location);
...
}
I tried to implement a unit test for this action method, but the value of the Response
property is null.
This was easy to solve in the previous versions of ASP.NET, because the Response
property had a setter, and you could simply set its value to a new HttpResponse
instance during a unit test.
But in ASP.NET 5 Response
does not have a setter, only a getter.
How can I set a value for Response
in a unit test?
UPDATE: Just to make clear: this question is about ASP.NET Core 1.0. The other question linked as duplicate is about ASP.NET 4, and the Api changed since then so the answer there does not apply to this question.
回答1:
I found one solution to the problem, however, it's a bit tedious and convoluted, so I'm still interested in seeing a simpler approach, if any exists.
The Controller
gets the value of its Response
property from ActionContext.HttpContext
. What makes mocking this difficult is that all these properties are read-only, so we cannot just simply set a mock value, we have to create mocks for every object in play.
The part of the Response I needed in my test was the Headers
collection, so I had to create and use the following mocks to make that availale. (Mocking is done with Moq.)
var sut = new MyController();
// The HeaderDictionary is needed for adding HTTP headers to the response.
// This needs a couple of different Mocks, because many properties in the class model are read-only.
var headerDictionary = new HeaderDictionary();
var response = new Mock<HttpResponse>();
response.SetupGet(r => r.Headers).Returns(headerDictionary);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(a => a.Response).Returns(response.Object);
sut.ActionContext = new ActionContext()
{
HttpContext = httpContext.Object
};
This is a bit more code than what I'd like to see to mock a single property, but I couldn't find any better approach yet, and it seems to be working nicely.
回答2:
You can use a ResponseWrapper with virtual properties and you can mock it and expect one call for virtual AddHeader method with "Location" parameter. Relevant Question Here: MOQ - Mocking MVC Controller's Response.Cookies.Clear()
You can check those examples: for ASP.Net 5 MVC 6:
- http://www.jerriepelser.com/blog/unit-testing-controllers-aspnet5
- http://dotnetthoughts.net/creating-unit-tests-for-asp-net-mvc-6-applications/
Another option if you want to try NancyFx(http://nancyfx.org/) (https://github.com/NancyFx/Nancy) it's quite easy: https://github.com/NancyFx/Nancy/wiki/Testing-your-application
For example - if you were testing a login page and wanted to verify that the user was redirected to an “error” page if they entered incorrect credentials you could use the ShouldHaveRedirectedTo assertion:
[Fact] public void Should_redirect_to_login_with_error_details_incorrect() { // Given var bootstrapper = new DefaultNancyBootstrapper(); var browser = new Browser(bootstrapper); // When var response = browser.Post("/login/", (with) => { with.HttpRequest(); with.FormValue("Username", "username"); with.FormValue("Password", "wrongpassword"); }); // Then response.ShouldHaveRedirectedTo("/login?error=true&username=username");
}
There is more:
[Fact] public void Should_display_error_message_when_error_passed() { // Given var bootstrapper = new DefaultNancyBootstrapper(); var browser = new Browser(bootstrapper); // When var response = browser.Get("/login", (with) => { with.HttpRequest(); with.Query("error", "true"); }); // Then response.Body["#errorBox"] .ShouldExistOnce() .And.ShouldBeOfClass("floatingError") .And.ShouldContain( "invalid", StringComparison.InvariantCultureIgnoreCase); }
来源:https://stackoverflow.com/questions/35319501/how-to-unit-test-a-controller-action-using-the-response-property-in-asp-net-5-m