Response.StatusCode is null in nunit testing - c#

i am writing unit test cases. I am trying to write unit test for this method but showing error. How to unit test this method in mvc3 framework and rhino mock.
public ActionResult UnderConstruction()
{
Response.StatusCode = (int)HttpStatusCode.TemporaryRedirect;
ErrorModel model = new ErrorModel()
{
ErrorMessage = "This page is still under construction; please check back later.",
Title = "Under Construction"
};
return View("Error", model);
}

It's the Response that is null, not Response.StatusCode. You need to mock HttpContextBase and HttpResponseBase, and then create and assign the controller's ControllerContext.
The test will look something like this (sorry if I fudge the Rhino Mock code; I use Moq normally):
// arrange
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
var request = MockRepository.GenerateMock<HttpRequestBase>();
var response = MockRepository.GenerateMock<HttpResponseBase>();
// stub both Request and Response, for good measure.
httpContext.Stub(x => x.Request).Return(request);
httpContext.Stub(x => x.Response).Return(response);
var controller = new YourController();
// create and assign the controller context
var context = new ControllerContext(httpContext,
new RouteData(),
controller);
controller.ControllerContext = context;
// act
var actual = controller.UnderConstruction() as ViewResultBase;
// assert
Assert.That(actual, Is.Not.Null);
Assert.That(controller.Response.StatusCode, Is.EqualTo(HttpStatusCode.TemporaryRedirect));
// etc.

Related

Mapper function in Web API Controller returns null during unit testing

I am making a ASP.NET core web api and I have used Entity Framework Core. I am trying to unit test the controllers using N-Unit
I am mocking the mapper and the repository. The APIs work fine and I have even created the front end or the API but I am having issues with the unit testing.
Controller Code-
[AllowAnonymous]
[HttpGet("{id}")]
public async Task<IActionResult> GetMovie(int id)
{
var movie = await _repo.GetMovie(id);
if (movie == null)
{
return BadRequest("Object with Id not found");
}
var movieToReturn = _mapper.Map<MovieForDetailedDto>(movie);
return Ok(movieToReturn);
}
[AllowAnonymous]
[HttpPost()]
public async Task<IActionResult> AddMovie(MovieForDetailedDto movieForDetailedDto)
{
if (await _repo.MovieExists(movieForDetailedDto.ATitle))
return BadRequest("movie already exists");
else if(!ModelState.IsValid || movieForDetailedDto.ATitle == null || movieForDetailedDto.APrice == null || movieForDetailedDto.AMovieDescription ==null)
{
return BadRequest("movie details not valid");
}
var movieToCreate = _mapper.Map<TblMovie>(movieForDetailedDto);
var createdMovie = await _repo.AddMovie(movieToCreate);
return Ok(createdMovie);
}
In all my functions, in the line where I map the DTO to the Model, the Line returns a null object during unit testing but the they work fine outside of unit testing.
My Unit Test Code for the controller-
[TestFixture]
public class MoviesControllerTests
{
private Mock<IMovieRepository> _mockMovieRepository;
private Mock<IMapper> _mockMovieMapper;
private MoviesController _moviesController;
[Test]
public async Task CallGetRequest_WhenCalledWithId_ReturnsTheMovieWithTheSameId()
{
getMoviesHelper getMoviesHelper = new getMoviesHelper();
List<TblMovie> movieList = getMoviesHelper.getMovieFromList();
var movie = getMoviesHelper.movieById(3);
_mockMovieRepository = new Mock<IMovieRepository>();
_mockMovieMapper = new Mock<IMapper>();
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>()))
.Returns(getMoviesHelper.movieById(3));
_mockMovieRepository.Setup(repo => repo.GetMovie(3))
.ReturnsAsync(getMoviesHelper.movieById(3));
_moviesController = new MoviesController(_mockMovieRepository.Object, _mockMovieMapper.Object);
var tblMovie = await _moviesController.GetMovie(3);
var okResult = tblMovie as OkObjectResult;
//Assert.AreEqual(200, okResult.StatusCode);
Assert.NotNull(tblMovie);
Assert.IsAssignableFrom<OkObjectResult>(tblMovie);
var result = ((OkObjectResult)tblMovie).Value;
var resultValue = ((OkObjectResult)tblMovie).Value as TblMovie;
Assert.AreEqual(resultValue.ATitle,"Raging Bull");
Assert.NotNull(result);
Assert.IsAssignableFrom<TblOrder>(result);
}
[Test]
public async Task GivenAValidMovie_WhenIPostANewMovie_ThenItReturnsOkWithResponse()
{
_mockMovieRepository = new Mock<IMovieRepository>();
_mockMovieMapper = new Mock<IMapper>();
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>()))
.Returns(new TblMovie());
_mockMovieRepository.Setup(repo => repo.AddMovie(It.IsAny<TblMovie>()))
.ReturnsAsync((TblMovie movie) => movie);
_moviesController = new MoviesController(_mockMovieRepository.Object, _mockMovieMapper.Object);
var tblMovie = await _moviesController.AddMovie(new MovieForDetailedDto
{
AMovieId = 55,
ATitle = "redemption",
AMovieDescription = "An action comedy adventure about brilliant robotics prodigy Hiro Hamada, who finds himself in the grips of a criminal plot that threatens to destroy the fast-paced, high-tech city of San Fransokyo. With the help of his closest companion-a robot named Baymax-Hiro joins forces with a reluctant team of first-time crime fighters on a mission to save their city.",
ADuration = "105 min",
APrice = "10",
APurchasePrice = "25",
ARating = 5,
AImageLink = "http://upload.wikimedia.org/wikipedia/en/4/4b/Big_Hero_6_%28film%29_poster.jpg",
ATrailerLink = "//www.youtube.com/embed/z3biFxZIJOQ",
AGenre = "Comedy",
AWideImage = "https://github.com/tushar23091998/MovieRentalApp-FrontEnd/blob/master/src/app/images/bighero6.jpg?raw=true"
});
var okResult = tblMovie as OkObjectResult;
Assert.AreEqual(200, okResult.StatusCode);
Assert.NotNull(okResult);
Assert.IsAssignableFrom<OkObjectResult>(tblMovie);
var result = ((OkObjectResult)tblMovie).Value;
Assert.NotNull(result);
Assert.IsAssignableFrom<TblMovie>(result);
}
In both the test cases and even in other test cases, the repo setup works fine but after defining the controller and when I call the controller function to get the value, I found in debugging that the mapper line in controller code returns null.
I am not sure how go about setting up the mockmapper now and how should I pass the value.
Debugging Output -
What we were doing in unit tests, when using AutoMapper, is instead of mocking it, rather initialize it, as descried here: https://kenbonny.net/2018/01/15/injecting-automapper-profiles-in-tests/
The cons I see doing this, is that your tests might be failing because of mapping issues and are not strictly testing the method.
Given your example, your setup is incorrect.
For the get method test, you should setup to return a MovieForDetailedDto for a TblMovie, but you are doing the opposite:
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>())).Returns(getMoviesHelper.movieById(3));
should be something like:
var expectedMovieDto = new MovieForDetailedDto(){//insert values here}
_mockMovieMapper.Setup(mapper => mapper.Map<MovieForDetailedDto>(It.IsAny<TblMovie>())).Returns(expectedMovieDto);
For the POST method, you are setting it up to return a new TblMovie (so all properties are set to their default values)
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>())) .Returns(new TblMovie());
should be:
var expectedMovie = new TblMovie()
{
ADuration = "some value",
AGenre = "some value"
//other }
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>())).Returns(expectedMovie);

Unit testing Controller Action when Response property is assigned inside of an action

I saw the very same issue in this post but none of the solutions works now in ASP.Net Core 2.2. When I debug the unit test the Response property is still null and thus the test fails.
I have been reading the asp.net core docs for an answer how to mock the ControllerContext so that the Response property has a value but I couldn't find anything working.
Here is the line inside the action that makes troubles:
Response.Headers.Add("Access-Control-Expose-Headers", "Content-Range");
So what I have ended up with in the unit test set up is:
var routeData = new RouteData();
routeData.Values.Add("controller", "Home");
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);
var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ControllerActionDescriptor());
_controller.ControllerContext = new ControllerContext(actionContext);
A lot of the setup can be avoided by using the DefaultHttpContext which would have the needed properties already populated. This includes the Response and its members
//..
var routeData = new RouteData();
routeData.Values.Add("controller", "Home");
var httpContext = DefaultHttpContext(); //<--
var actionContext = new ActionContext(
httpContext,
routeData,
new ControllerActionDescriptor());
_controller.ControllerContext = new ControllerContext(actionContext);
//...
After exercising the subject under test, the response can the obtained from the context used by the controller and desired behavior asserted.
//...
//Assert
var response = httpContext.Response;
var key = "Access-Control-Expose-Headers";
Assert.True(response.Headers.TryGetValues(key, out var value));
Assert.Equals("Content-Range", value.FirstOrDefault()
Finally I have managed to mock the Controller's HttpContext with the following code:
protected void SetUpControllerContext(ClaimsPrincipal principal)
{
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);
httpContext.SetupGet(a => a.User).Returns(principal);
this.SutController.ControllerContext = new ControllerContext()
{
HttpContext = httpContext.Object
};
}
Now altering the Response property inside Controller's actions is allowed and doesn't trigger an error.

How to Assert StatusCode from raw POST request in my unit tests?

I am using ASP.NET Core 2 WebAPI, xUnit, Moq.
I have a problem. I was trying the solution from this answer and this answer. Other found answers seem to be similar to these two.
With both mentioned approaches we are using casting of the IActionResult result object to ObjectResult or OkObjectResult.
The main problem with my project is that after casting object result is null.
Test method:
[Fact]
public async Task Post_CalledWithDefaultSetup_CallsAllMethods()
{
//default setup arranged in the ctor
IActionResult result = await _controller.Post();
var okResult = result as ObjectResult;
Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode);
}
result object on debug:
And tested method:
[HttpPost]
public async Task<IActionResult> Post()
{
//do some logic here
return StatusCode((int)HttpStatusCode.OK); //debug hits this
}
Also in my test class ctor I am arranging my mocks and ControllerContext:
_serializationServiceMock.Setup(s => s.UnzipData(_array))
.Returns(_jsonExample);
_serializationServiceMock.Setup(s => s.DeserializeDataFromJson(_jsonExample))
.Returns(_listPersonModel);
var stream = new MemoryStream(_array);
var httpContext = new DefaultHttpContext();
httpContext.Request.Body = stream;
httpContext.Request.ContentLength = stream.Length;
var controllerContext = new ControllerContext()
{
HttpContext = httpContext,
};
_controller = new DataController(_repositoryMock.Object, _serializationServiceMock.Object) { ControllerContext = controllerContext };
In your code block, the controller method returns StatusCodeResult type. But you try to cast it to ObjectResult. That's why you get null.
You can solve this in 2 ways;
1 -) You can use return Ok() instead of return StatusCode(...) so your controller method will return OkResult and with parametered call Ok(someObj) it will return OkObjectResult.
2 -) you can cast Post result to StatusCodeResult instead of ObjectResult since your endpoint returns status code result like below.
var okResult = result as StatusCodeResult;

Unit testing WebApi controllers in WebApi

I am trying to unit test my controller, but as soon as this controller uses its embedded UrlHelper object, it throws an ArgumentNullException.
The action I'm trying to test is this one:
public HttpResponseMessage PostCommandes(Commandes commandes)
{
if (this.ModelState.IsValid)
{
this.db.AddCommande(commandes);
HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.Created, commandes);
// this returns null from the test project
string link = this.Url.Link(
"DefaultApi",
new
{
id = commandes.Commande_id
});
var uri = new Uri(link);
response.Headers.Location = uri;
return response;
}
else
{
return this.Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
My test method looks like this:
[Fact]
public void Controller_insert_stores_new_item()
{
// arrange
bool isInserted = false;
Commandes item = new Commandes() { Commande_id = 123 };
this.fakeContainer.AddCommande = (c) =>
{
isInserted = true;
};
TestsBoostrappers.SetupControllerForTests(this.controller, ControllerName, HttpMethod.Post);
// act
HttpResponseMessage result = this.controller.PostCommandes(item);
// assert
result.IsSuccessStatusCode.Should().BeTrue("because the storage method should return a successful HTTP code");
isInserted.Should().BeTrue("because the controller should have called the underlying storage engine");
// cleanup
this.fakeContainer.AddCommande = null;
}
And the SetupControllerForTests method is this one, as seen here:
public static void SetupControllerForTests(ApiController controller, string controllerName, HttpMethod method)
{
var request = new HttpRequestMessage(method, string.Format("http://localhost/api/v1/{0}", controllerName));
var config = new HttpConfiguration();
var route = WebApiConfig.Register(config).First();
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary
{
{
"controller",
controllerName
}
});
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
}
This is a pretty well documented problem for WebApi2, you can read more about it here for instance ("testing link generation"). Basically, it boils down to either setting a custom ApiController.RequestContext, or mocking the controller's Url property.
The problem is that, in my version of WebApi (Nuget packages: Microsoft.AspNet.WebApi 4.0.20710.0 / WebApi.Core.4.0.30506.0), ApiController.RequestContext does not exist, and Moq cannot mock the UrlHelper class, because the method it should mock (Link) is not overridable, or something like that (I didn't dwell on it). Because I'm using WebApi 1. But the blog post I based my code on (as well as many other posts) use V1, too. So I don't understand why it doesn't work, and most of all, how I can make it work.
Thank you !
Not sure if the documentation you linked to was updated since your original post but they show an example where they mock up the UrlHelper and also the Link method.
[TestMethod]
public void PostSetsLocationHeader_MockVersion()
{
// This version uses a mock UrlHelper.
// Arrange
ProductsController controller = new ProductsController(repository);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
string locationUrl = "http://location/";
// Create the mock and set up the Link method, which is used to create the Location header.
// The mock version returns a fixed string.
var mockUrlHelper = new Mock<UrlHelper>();
mockUrlHelper.Setup(x => x.Link(It.IsAny<string>(), It.IsAny<object>())).Returns(locationUrl);
controller.Url = mockUrlHelper.Object;
// Act
Product product = new Product() { Id = 42 };
var response = controller.Post(product);
// Assert
Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}
So, you need to mock UrlHelper.Link method. It can be easily done with Typemock Isolator (test example from given link):
[TestMethod, Isolated]
public void PostSetsLocationHeader_MockVersion()
{
// This version uses a mock UrlHelper.
// Arrange
ProductsController controller = new ProductsController(repository);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
string locationUrl = "http://location/";
// Create the mock and set up the Link method, which is used to create the Location header.
// The mock version returns a fixed string.
var mockUrlHelper = Isolate.Fake.Instance<UrlHelper>();
Isolate.WhenCalled(() => mockUrlHelper.Link("", null)).WillReturn(locationUrl);
controller.Url = mockUrlHelper;
// Act
Product product = new Product() { Id = 42 };
var response = controller.Post(product);
// Assert
Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}

ControllerContext.HttpContext initialization for unit tests

In the process of writing an unit test for a Controller, I need to setup or initialize -
ControllerContext.HttpContext.Request.QueryString
What is the simplest way for setting this up so that I can actually pass the - ControllerContext.HttpContext.Request.QueryString - and have the controller tested?
You could use a mock framework in order to mock the HttpContext that the controller is working with.
For example with NSubstitute:
// arrange
var contextMock = Substitute.For<HttpContextBase>();
var requestMock = Substitute.For<HttpRequestBase>();
var queryString = new NameValueCollection();
queryString["foo"] = "bar";
requestMock.QueryString.Returns(queryString);
contextMock.Request.Returns(requestMock);
var sut = new SomeController();
sut.ControllerContext = new ControllerContext(contextMock, new RouteData(), sut);
// act
var actual = sut.SomeAction();
// assert
...
Of course you could have used any other mocking framework you like such as Moq or Rhino Mocks in order to mock the HttpContext.
By the way if you used view models (which you should always do in a properly designed ASP.NET MVC application) you wouldn't even cared about Request.QueryString because your controller action would now have taken a view model as argument:
public ActionResult SomeAction(MyViewModel model)
{
...
}
and the default model binder would have taken into account the binding of request parameters to model. You should avoid writing plumbing code in your controller action where you are extracting some data from query strings and stuffs.
Look how much more readable your unit test now becomes:
var model = new MyViewModel();
model.Foo = "bar";
var sut = new SomeController();
// act
var actual = sut.SomeAction(model);
// assert
...
See? You don't need to care about plumbing code anymore.

Categories

Resources