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

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;

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

Dealing with TempData variables in Unit Tests

Following the example from here:
Mocking a TempData in ASP.NET Core in MSTest,
I wrote down the following TestMethod:
[Fact]
public void TestBackMethod()
{
var httpContext = new DefaultHttpContext();
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
tempData["id"] = 3008;
var controller = new PhaseController(Configuration)
{
TempData = tempData
};
var result = controller.Back() as ViewResult;
Assert.Contains("Index", result.ViewName);
}
For this Controller Method:
public IActionResult Back()
{
int releaseId = (int)TempData["id"];
return RedirectToAction("Index", "Phase", new { id = releaseId });
}
However, on this line:
Assert.Contains("Index", result.ViewName);
result is null.
'Object reference not set to an instance of an object.'
Why is this happening and how can I fix it?
Because Back doesn't return a ViewResult - it returns a RedirectToActionResult (you can hover over RedirectToAction to see the exact object name). Both of these implement IActionResult.
You get a Null Reference Exception because when you use the as keyword for casting objects it will return null if the conversion is not possible.
If you instead had
var result = (ViewResult)controller.Back();
You would get a difference exception during run time of that line saying the conversion was not possible.
You should do the conversion doing one of these methods:
var result = (RedirectToActionResult)controller.Back();
or
var result = controller.Back() as RedirectToActionResult;

How to test Ok() result using MStest in ASP.NET Core project

I'm using MStest for testing my controllers.
I want to test this action:
[HttpGet(Name = "GetGroups")]
public async Task<IActionResult> Get()
{
var groups = await _unitOfWork.Repository<Groupe>().GetAllAsync();
var groupsDto = Mapper.Map<IEnumerable<GroupDto>>(groups);
if (groupsDto.Count() == 0)
{
return NotFound();
}
return Ok(groupsDto);
}
One of the test for this action looks like that:
[TestMethod]
public async Task Group_Get_Should_Return_InstanceOfTypeOkNegotiatedContentResultIEnumerableGroupDto()
{
// Arrange
moqGroupRepository.Setup(g => g.GetAllAsync(null)).ReturnsAsync(groups).Verifiable();
moqUnitOfWork.Setup(x => x.Repository<Groupe>()).Returns(moqGroupRepository.Object);
var controller = new GroupController(moqUnitOfWork.Object);
// Act
var actionResult = await controller.Get() as OkNegotiatedContentResult<IEnumerable<GroupDto>>;
// Assert
Assert.IsInstanceOfType(actionResult, typeof(OkNegotiatedContentResult<IEnumerable<GroupDto>>));
}
The problem here is OkNegotiatedContentResult is unknown in ASP.Net Core project test.
What should I use to test Ok() result?
The probleme here is OkNegotiatedContentResult is unknow in asp net
core project test What should i use to test Ok() result?
You could fix the problem by installing Microsoft.AspNetCore.Mvc NuGet package where implementations of IActionResult are defined.
However ASP.NET Core does not contain OkNegotiatedContentResult type, it's from ASP.NET Web API. In ASP.NET Core Controller.Ok() method returns the instance of OkObjectResult type.
You also have inconsistent checks in these two statements:
var actionResult = await controller.Get() as OkNegotiatedContentResult<IEnumerable<GroupDto>>;
Assert.IsInstanceOfType(actionResult, typeof(OkNegotiatedContentResult<IEnumerable<GroupDto>>));
as operator will return null if object could not be cast to requested type. So you could replace the second check with the following:
Assert.IsNotNull(actionResult);
So the required steps are:
Install Microsoft.AspNetCore.Mvc NuGet package to your Test project.
Adjust the test code in the following way:
// ...
using Microsoft.AspNetCore.Mvc;
[TestMethod]
public async Task Group_Get_Should_Return_InstanceOfTypeOkNegotiatedContentResultIEnumerableGroupDto()
{
// Arrange
moqGroupRepository.Setup(g => g.GetAllAsync(null)).ReturnsAsync(groups).Verifiable();
moqUnitOfWork.Setup(x => x.Repository<Groupe>()).Returns(moqGroupRepository.Object);
var controller = new GroupController(moqUnitOfWork.Object);
// Act
var actionResult = await controller.Get() as OkObjectResult;
// Assert
Assert.IsNotNull(actionResult);
Assert.IsInstanceOfType(actionResult.Value, typeof(IEnumerable<GroupDto>));
}
Code Fuller's post helps. The last Assert should be a little different, maybe because we are on 3.0 now:
Assert.IsInstanceOfType(typeof(IEnumerable<GroupDto>), actionResult.Value);

Assert for empty content in 200 Http Response

I have a simple Web Api method which returns a list. I decided as a general project rule, that if the list is empty for a particular userId, we return an Ok() method with empty content.
My web api method looks like following:
[Route("")]
[HttpGet]
public IHttpActionResult GetPersonalList()
{
var result = _facade.Get(_userContext.Get());
if (result == null)
return Ok(); //here is the point
return Ok(new PersonalExpensesReportViewModel(result));
}
Trying to make a 100% of coverage of this method, I wanted to test the scenario that I mentioned, but I could not achieve how to write the assert for the empty content.
[TestMethod]
public void GetPersonalList_NoContent_Ok()
{
//Arrange
_facade.Setup(x => x.Get(_userContext.Object.GetPersonnelNumber(), null)).Returns((PersonalExpensesReport)null);
//Act
var result = _controller.GetPersonalList();
//Assert
var negociatedResult = result as OkResult;
Assert.IsNotNull(result);
// ?? I want something like Assert.IsNull(negociatedResult.Content)
}
Being that I don't have a certain type to make result as OkNegotiatedContentResult which expects T type to be instantiated, I thought about cast as OkResult, but I don't have the 'Content' property in this class.
Does someone know how to proceed in this cases?
Please try to use OkNegotiatedContentResult<T> like:
var result = _controller.GetPersonalList();
var response = result as OkNegotiatedContentResult<PersonalExpensesReportViewModel>;
Assert.IsNotNull(response);
var content = response.Content;
Assert.AreEqual(5, content.Count());
[TestMethod]
public void GetPersonalList_NoContent_Ok()
{
var serviceresponse = new yourresponseobject<yourmodel>{
Message = "what ever response";
Data = null;
};
var service = new Mock<youserviceInterface>(MockBehavior.Strict);
service.Setup(x => x.GetPersonalList()(It.IsAny<string>())).ReturnsAsync(serviceResponse); /// for number of parameters in controller method, add It.IsAny<string>()
//Arrange
_facade.Setup(x => x.Get(_userContext.Object.GetPersonnelNumber(), null)).Returns((PersonalExpensesReport)null);
//Act
var result = _controller.GetPersonalList();
//Assert
var negociatedResult = result as Object;
Assert.IsNotNull(result.value);
Assert.AreEqual(200,result.negociatedResult.statuscode);
}

Response.StatusCode is null in nunit testing

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.

Categories

Resources