ViewResult.StatusCode is null despite explicitly setting it - c#

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!

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;
}

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
);
}

Related

UnitTest HttpResponse WriteAsync and CopyToAsync

I would like to unit test the next method:
public static async Task SetResponseBody(HttpResponse response, string message)
{
var originalResponseBody = response.Body;
var responseBody = new MemoryStream();
response.Body = responseBody;
response.ContentType = "application/json";
dynamic body = new { Message = message };
string json = JsonSerializer.Serialize(body);
await response.WriteAsync(json);
response.Body.Seek(0, SeekOrigin.Begin);
await responseBody.CopyToAsync(originalResponseBody);
}
The last two lines are used from this post.
The current unit test implementation is:
[TestMethod]
public async Task SetResponseBody_TestMessageAsync()
{
var expected = "TestMessage";
string actual = null;
var responseMock = new Mock<HttpResponse>();
responseMock
.Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Callback((byte[] data, int offset, int length, CancellationToken token) =>
{
if (length > 0)
actual = Encoding.UTF8.GetString(data);
})
.Returns(Task.CompletedTask);
await ResponseRewriter.SetResponseBody(responseMock.Object, expected);
}
The unit tests fails due to a NullReferenceException which is raised once the test hits the 'await response.WriteAsync(json);' line of code. Could you point me in the right direction in order to fix this exception, so the test will pass?
Summarized: The unit tests needs to check if the given 'TestMessage' is actually written to the Body of the response.
Background information:
I'm calling the SetResponseBody method in order to modify the response body as soon as the 'OnRedirectToIdentityProvider' event is raised from AddOpenIdConnect.
OnRedirectToIdentityProvider = async e =>
{
// e is of type RedirectContext
if (e.Request.Path.StartsWithSegments("/api")))
{
if (e.Response.StatusCode == (int)HttpStatusCode.OK)
{
e.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
// TestMessage is a const
// e.Response is readonly (get) so it's not possible to set it directly.
await ResponseRewriter.SetResponseBody(e.Response, TestMessage);
}
e.HandleResponse();
}
await Task.CompletedTask;
}
.NET Core 3.1, WebApi, OpenId
There are too many internals that need to be configured for the abstract HttpResponse to work as intended when mocking it.
I would suggest using DefaultHttpContext and extracting the default response created within that context.
[TestMethod]
public async Task SetResponseBody_TestMessageAsync() {
//Arrange
string expected = "TestMessage";
string actual = null;
HttpContext httpContext = new DefaultHttpContext();
HttpResponse response = httpContext.Response
//Act
await ResponseRewriter.SetResponseBody(response, expected);
//Assert
//...
}
for the assertion, extract the content of the response body and assert its expected behavior.
We can't write unit test for all method especially system library.
so solution is make virtual function and use below method instead of direct await response.WriteAsync(json);
public virtual async Task WriteAsync(string json)
{
await response.WriteAsync(json);
}
and then
yourClassObject_Where_SetResponseBody_Exist.Setup(m => m.WriteAsync
(It.IsAny<string>()));

Unit Testing: How to return value asynchronously from mocked interface

I'm trying to unit test a method and assert that the result is of a specific type when calling a mocked 'request provider' which handles HttpClient logic, though I have setup the mocked interface it always returns null.
Previously when using HttpClient I've mocked the HttpMessageHandler and handled the business logic in the method at the other side, however the third party API we are using requires multiple calls to their rest api using GET requests, so I wanted a solution that kept the solution more 'DRY'
The following is the setup I am currently trying to use
_requestProvider.Setup(
svc => svc.GetAsync<PlayerBalance>(It.IsAny<string>(), It.IsAny<string>()))
.Returns(() => Task.FromResult(new PlayerBalance
{
Balance = 0
}));
_playerService = new PlayerService(_playerRepository.Object,
_secretsService.Object,
_awsConfig,
_requestProvider.Object);
My act/assertion
var result = await _playerService.GetPlayerBalanceAsync(request);
result.Should().BeOfType<PlayerBalance>();
The method under test
public async Task<PlayerBalance> GetPlayerBalanceAsync(PlayerBalanceRequest playerBalanceRequest)
{
if (string.IsNullOrEmpty(playerBalanceRequest.Login)) throw new Exception("Login is a required parameter.");
string url = $#"myrestendpoint";
var result = await _requestProvider.GetAsync<List<PlayerBalance>>(url);
return result.FirstOrDefault();
}
And where it's failing invocation on the mock
public async Task<TResult> GetAsync<TResult>(string uri, string token = "")
{
HttpClient httpClient = CreateHttpClient(token);
HttpResponseMessage response = await httpClient.GetAsync(uri);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));
return result;
}
When running in 'Strict' it is telling me that it expected invocation but no setup was found. I'm not really sure what else needs setting up here.
the result returned from the last slice of code is always null, but I want it to be PlayerBalance { Balance = 0 }
Any help would be appreciated.
Just to clarify I have also tried my setup in the following ways
.Returns(Task.FromResult(new PlayerBalance
{
Balance = 0
}));
.ReturnsAsync(new PlayerBalance {
Balance = 0
});
You are mocking:
_requestProvider.GetAsync<PlayerBalance>(url)
When you should be mocking:
_requestProvider.GetAsync<List<PlayerBalance>>(url)
Setup the mock to expect the desired behavior:
_requestProvider.Setup(
svc => svc.GetAsync<List<PlayerBalance>>(It.IsAny<string>(), It.IsAny<string>())
)
.ReturnsAsync(() => new List<PlayerBalance>() { new PlayerBalance
{
Balance = 0
}});

Automapper in Webapi Controller Unit test

I have a controller method in my webapi project which calls a service method and converts the response of the service method into a Dto object for sending the response.
The controller looks something like this:
[HttpPost]
[Route(WebApiConfig.RootApiUri + "/v1/examplepost")]
public async Task<List<Html1Dto>> examplepost([FromBody] SomeInfoDto someInfoDto)
{
var someInfo = _mapper.Map<SomeInfoDto, SomeInfo>(someInfoDto);
return this._mapper.Map<List<Html1>, List<Html1Dto>>(await this._someService.SomeCall(someInfo));
}
and the mock test like this :
//Arrange
var mockMapper = new Mock<IMapper>();
var mockSomeService = new Mock<ISomeService<Html1>>();
mockSomeService.Setup(s => s.SomeCall(It.IsAny<SomeInfo>())).ReturnsAsync(
new List<Html1>() {new Html1() {....}});
SomeInfoDto mockSomeInfoDto = new SomeInfoDto()
{
..
};
SomeInfo mockSomeInfo = new SomeInfo();
mockMapper.Setup(m => m.Map<SomeInfoDto, SomeInfo>(mockSomeInfoDto))
.Returns(mockSomeInfo);
mockMapper.Setup(m => m.Map<List<Html1>, List<Html1Dto>>(It.IsAny<List<Html1>>())).Returns(It.IsAny<List<Html1Dto>>);
var someController = GetController(mockMapper.Object, mockSomeService.Object);
//Act
var result = await someController.examplePost(mockSomeInfoDto);
I am using automapper to map the objects with Dtos. When I debug this test, result comes as null. The mapping of incoming dto works fine. I suspect there is some issue with the service method setup. Anyhelp is appreciated.
Your mapper mock is the other way round
mockMapper.Setup(m => m.Map<List<Html1>, List<Html1Dto>>(It.IsAny<List<Html1>>())).Returns(It.IsAny<List<Html1Dto>>);
to the signature in the method
this._mapper.Map<List<Html1Dto>, List<Html1>>(await this._someService.SomeCall(someInfo));
Additionally, assuming that is correct in your actual code, then the other bit that could be causing you issue is that the return It.IsAny<List<Html1Dto>> which will be null as default(List<HtmlDto>) is null, return a concrete class there instead as below.
This call:
this._mapper.Map<List<Html1Dto>, List<Html1>>(await this._someService.SomeCall(someInfo));
Doesn't have a setup in the Unit Test, so will return null. You need to arrange that to, probably something like:
mockMapper.Setup(m => m.Map<List<Html1Dto>, List<Html1>>(It.IsAny<List<Html1>>()))
.ReturnsAsync(new List<Html1Dto> { ... });

Invoking WebApi's another action in another controller via reflection ?

We have many services in our system. ( integrating with a mobile company)
So, (for example) we have :
Action1 in Controller1
Action2 in Controller1
...
Action4 in Controller4
Action5 in Controller4
...
Currently, the mobile company calls each action with a single request.
But recently they told us , "can we send you a list of Actions to invoke ? instead of running single action manually each time... ?"
So I tried reflection:
ServicesController :
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage AAA( )
{
Type type = typeof(UsersController);
var instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Test2", BindingFlags.Instance | BindingFlags.Public);
var t= method.Invoke(instance, new object[] { "royi" });
return Request.CreateResponse(HttpStatusCode.OK, t);
}
And :
UseresController :
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage Test2( string ggg)
{
return Request.CreateResponse(HttpStatusCode.OK, "hello"+ggg);
}
When I run via fiddler :
http://es.com/api/services/aaa ( GET)
It does work , but (obviously) the Request on the other side is null :
Question
How can I make Test2 run as expected ? am I on the right direction of solving this ? or does webApi has built in mechanism for this sort of thing ?
You better use the ActionInvoker to do that:
public HttpResponseMessage AAA()
{
var ctrlDesc = new HttpControllerDescriptor(this.Configuration, "UsersController", typeof(UsersController));
var actionDesc = new ReflectedHttpActionDescriptor(ctrlDesc, typeof(UsersController).GetMethod("Test2"));
var ctrlCtx = new HttpControllerContext(this.Configuration, this.Request.GetRouteData(), this.Request);
var apiCtrl = ctrlDesc.CreateController(this.Request) as ApiController;
apiCtrl.Request = this.Request;
apiCtrl.Configuration = this.Configuration;
apiCtrl.ControllerContext = ctrlCtx;
ctrlCtx.Controller = apiCtrl;
ctrlCtx.ControllerDescriptor = ctrlDesc;
ctrlCtx.Request = this.Request;
ctrlCtx.RouteData = this.Request.GetRouteData();
var actionContext = new HttpActionContext(ctrlCtx, actionDesc);
actionContext.ActionArguments.Add("ggg", "royi");
var invoker = this.Configuration.Services.GetActionInvoker();
return invoker.InvokeActionAsync(actionContext, CancellationToken.None).Result;
}

Unit test dependencies for an ASP.NET MVC 3 ViewResult subclass

I'm creating a really simple ViewResult subclass called JavaScriptViewResult that, when executing, calls the base implementation and then sets the Content-Type of the response to text/javascript. In trying to unit test this class, I'm running across a slew of difficulties fulfilling all of the dependencies of the ASP.NET MVC stack.
Here is what my unit test, which uses Rhino, looks like so far:
[TestMethod]
public void TestExecuteAction()
{
var request = MockRepository.GenerateMock<HttpRequestBase>();
request.Expect(m => m.Url).Return(new Uri("/Test/JavaScript", UriKind.Relative));
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
httpContext.Expect(m => m.Request).Return(request);
var controller = MockRepository.GenerateMock<ControllerBase>();
var virtualPathProvider = MockRepository.GenerateMock<VirtualPathProvider>();
var routeCollection = new RouteCollection(virtualPathProvider);
routeCollection.MapRoute("FakeRoute", "Test/JavaScript", new { controller = "Test", action = "JavaScript" });
var routeData = routeCollection.GetRouteData(httpContext);
var context = new ControllerContext(httpContext, routeData, controller);
var viewResult = new JavaScriptViewResult();
viewResult.ExecuteResult(context);
Assert.AreEqual("text/javascript", context.HttpContext.Response.ContentType);
}
The latest exception when running the test is a NullReferenceException deep within the bowels of System.Web.Routing.Route.GetRouteData(HttpContextBase httpContext).
How do I set up all of the dependencies for executing a ViewResult? Are there any techniques for making this simpler? Alternately, is there a different way I can utilize the MVC view engine to generate JavaScript that will set the proper Content-Type for the response?
I figured out how to meet the minimum requirements of ViewResult. One problem I was encountering was mocking the process of finding the view. This was avoidable by ensuring that the View property of my object was populated. Here is my working test:
[TestMethod]
public void TestExecuteAction()
{
var response = MockRepository.GenerateStub<HttpResponseBase>();
response.Output = new StringWriter();
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
httpContext.Expect(m => m.Response).Return(response);
var routeData = new RouteData();
routeData.Values.Add("action", "FakeAction");
var context = new ControllerContext(httpContext, routeData, MockRepository.GenerateMock<ControllerBase>());
var viewResult = new JavaScriptViewResult();
viewResult.View = MockRepository.GenerateMock<IView>();
viewResult.ExecuteResult(context);
Assert.AreEqual("text/javascript", context.HttpContext.Response.ContentType);
}

Categories