Dealing with TempData variables in Unit Tests - c#

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;

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

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;

C# From ObjectResult to string (or get value)

I'm using a IActionResult (task) to upload a file and i refference that in my controller. What i want to get back is the file name.
Controller ->
var imageLocation = await _imageHandler.UploadImage(image);
ImageHandler ->
public async Task<IActionResult> UploadImage(IFormFile file)
{
var result = await _imageWriter.UploadImage(file);
return new ObjectResult(result);
}
My value is stored in imageLocation, but i have no idea how to access it (i need the "Value" string so i can add it to DB).
I've tried searching for everything, but everyone is using a list. I only need a string here.
Hopefully you guys can help me out. Thanks!
You can cast the result to the desired type and call property
Controller
var imageLocation = await _imageHandler.UploadImage(image);
var objectResult = imageLocation as ObjectResult;
var value = objectReult.Value;
or just refactor the ImageHandler.UploadImage function to return the actual type to avoid having to cast
public async Task<ObjectResult> UploadImage(IFormFile file) {
var result = await _imageWriter.UploadImage(file);
return new ObjectResult(result);
}
and get the value as expected in controller
var imageLocation = await _imageHandler.UploadImage(image);
var value = imageLocation.Value;
Better yet, have the function just return the desired value
public Task<string> UploadImage(IFormFile file) {
return _imageWriter.UploadImage(file);
}
that way you get what is expected when calling the function in the controller.
string imageLocation = await _imageHandler.UploadImage(image);
Just came across a Result.Value from FluentValidation Errors:
ObjectResult? result = (await controller.CreateSomething(InvalidDTO)) as ObjectResult;
List<FluentValidation.Results.ValidationFailure> value = (List<FluentValidation.Results.ValidationFailure>)result.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);
}

Why is validation not happening correctly on my model? [duplicate]

This question already has answers here:
Testing ModelState is always valid in asp.net mvc
(7 answers)
Closed 7 years ago.
ModelState.IsValid is being set to true when the properties are incorrect. I've decorated the fields with Requied, Minimum/MaxLength etc however the ModelState.IsValid bool is returning as true.
Is this because im skipping the model binding as i'm testing and it doesnt actually perform validation?
[Authorize(Roles = "A")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddProject(Projects project)
{
if (project == null)
{
return HttpNotFound();
}
if (ModelState.IsValid)
{
using (var db = new DbContext())
{
db.ProjectModels.Add(project);
db.SaveChanges(); //exception raised here.
}
return RedirectToAction("ListOfProjects", "Project");
}
return View("AddProject", project);
}
[TestMethod()]
public void AddProjectTestWithModel()
{
//initialize
var controller = new AdminController();
var model = new Projects()
{
Name = "Project",
Description = "Description",
Duration = "1 Month"
};
var nullModel = new Projects();
nullModel = null;
var invalidModel = model;
invalidModel.Description = null;
invalidModel.Name = null;
//setup
var result = (RedirectToRouteResult)controller.AddProject(model) as RedirectToRouteResult;
var modelFromDb = db.ProjectModels.Find(model.Id);
var result2 = (HttpNotFoundResult)controller.AddProject(nullModel) as HttpNotFoundResult;
var result3 = (ViewResult)controller.AddProject(invalidModel) as ViewResult;
Assert.AreEqual("ListOfProjects", result.RouteValues["action"]);
Assert.AreEqual(404, result2.StatusCode);
Assert.AreEqual("AddProject", result3.ViewName); //UnitTest fails here.
}
Any idea why? I expect the result3 to be of ViewResult and the ViewName to be "AddProject" because ModelState.IsValid bool should be false. Why? :(
Found the solution. Validation happens when the data that is posted is bound to view model. This is being skipped when ibis passed straight into the controller. The viewmodel is what is passed into the controller and once the model is posted and bound is when the validation is checked, well before it goes into the controller, thats why break pointing in the code will never break when testing the form, because it doesnt even reach the controller.
Hope that made sense if you ever have a similar problem.

Categories

Resources