How to Unit test WEB API Controller Exception using Moq - c#

How to Unit test IHttpActionResult Exception InternalServerError with status code 500 and message
[HttpGet]
public async Task<IHttpActionResult> Get(Guid myId)
{
try
{
var myaccount = await _myaccountService.GetMyAccount(myId);
return Ok(myaccount);
}
catch (Exception ex)
{
return InternalServerError();
}
}
Did try with Test method
[TestMethod]
public async Task GeMyAccount_WhenThrowsException_ReturnsServerError()
{
// Arrange
var exception = new Exception("Internal Server Error");
var expectedResult = new List<MyAccount>
{
new MyAccount
{
Id = "1",
Name = "Name1"
},
new MyAccount
{
Id = "2",
Name = "Name2"
},
};
var myId = new Guid();
//Act
var mockMyAccountService = new Mock<IMyAccountService>();
mockMyAccountService.Setup(mock =>
mock.GetMyAccount(myId)).Throws(exception).Verifiable();
var controller = new MyAccountController(mockMyAccountService.Object);
//Assert
var actualResult = (await controller.Get(myId) as
OkNegotiatedContentResult<MyAccount>).Content;
?? var result = actualResult as ObjectResult;
?? Assert.AreEqual(StatusCodes.Status500InternalServerError, result.StatusCode);
?? Assert.AreEqual("Internal Server Error ", result.Value);
mockMyAccountService.Verify(b => b.GetMyAccount(myId));
}
Not sure how to get the Exception and status code 500 using Moq.

As it was said by Nkosi the exception is swallowed by the try-catch block so you are not able to make any assertion against the exception.
But you can (and should) make assertion against the returned object.
[TestMethod]
public async Task GivenAFaultyMyAccountService_WhenICallGet_ThenItReturnsAnInternalServerError()
{
//Arrange
var expectedException = new Exception("Something went wrong");
var mockMyAccountService = new Mock<IMyAccountService>();
mockMyAccountService
.Setup(svc => svc.GetMyAccount(It.IsAny<Guid>()))
.Throws(expectedException);
var sut = new MyAccountController(mockMyAccountService.Object);
//Act
var actualResult = await sut.Get(Guid.NewGuid());
//Assert
Assert.IsInstanceOfType(actualResult, typeof(InternalServerErrorResult));
}
I've made the following changes to your test:
I've renamed your test to align with the Given-When-Then structure
In this test case I'm only focusing on a single situation when the underlying dependency is malfunctioning, so I've get rid of everything which is related to the happy path
The happy path should have a separate test case
I've made the mock Setup more generic by replacing the myId parameter to It.IsAny<Guid>()
I've also replaced the new Guid() to Guid.NewGuid() because the former would create an empty uuid, while the later will generate a new uuid
I've removed the Verifiable call because it is not really needed here
The Act phase is when you make the actual call against a given method, not when you are constructing the controller, so I've moved the comment to the right place
I've changed the variable name controller to sut, which stands for the System Under Test
I've replaced your hard to read assessment logic to a simple type check
InternalServerError returns an InternalServerErrorResult
MSTest's Assert class has a method called IsInstanceOf, which makes the type check more convenient

Take a look at this code
[Fact]
public async Task Test1()
{
//arrange
//prepare your data for test
//act
async Task action()
{
//run your sut action
}
//assert
var exception = await Assert.ThrowsAsync<Exception>(action);
Assert.Equal("Internal Server Error", exception.Message);
}

Related

Unit test FluentEmail in .NET 5 using xUnit and Moq

I'm trying to unit test FluentEmail, but I keep getting null in the send response.
This is how it's set up in the ConfigureServices method:
// Set email service using FluentEmail
services.AddFluentEmail("sender#test.com")
// For Fluent Email to find _Layout.cshtml, just mention here where your views are.
.AddRazorRenderer(#$"{Directory.GetCurrentDirectory()}/Views/")
.AddSmtpSender("smtp.somecompanyname.com", 25)
.AddSmtpSender(new System.Net.Mail.SmtpClient() { });
Now the email service looks like this:
public class FluentEmailService : IFluentEmailService
{
private readonly IFluentEmail _fluentEmail;
private readonly ILogger<FluentEmailService> _logger;
public FluentEmailService(ILogger<FluentEmailService> logger, IFluentEmail fluentEmail)
{
_logger = logger;
_fluentEmail = fluentEmail;
}
public async Task<SendResponse> SendEmailAsync<TModel>(string subject, string razorTemplatePath, TModel model, string semicolonSeparatedEmailRecipients)
{
try
{
var sendResponse = await _fluentEmail
.To(semicolonSeparatedEmailRecipients)
.Subject(subject)
.UsingTemplateFromFile(razorTemplatePath, model)
.SendAsync();
return sendResponse;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send email. Check exception for more information.");
return new SendResponse() { ErrorMessages = new string[] { ex.Message } };
}
}
}
My test method looks like this:
[Fact]
public async Task Can_Verify_FluentEmail_WORKS_Async()
{
// ARRANGE
var mockFluentEmail = new Mock<IFluentEmail>();
mockFluentEmail.Setup(m => m.To(It.IsAny<string>())).Returns(mockFluentEmail.Object);
mockFluentEmail.Setup(m => m.Subject(It.IsAny<string>())).Returns(mockFluentEmail.Object);
mockFluentEmail.Setup(m => m.UsingTemplateFromFile(It.IsAny<string>(), It.IsAny<It.IsAnyType>(), It.IsAny<bool>())).Returns(mockFluentEmail.Object);
//Create FluentEmail service using fake logger and IFluentEmail
var fakeLogger = Mock.Of<ILogger<FluentEmailService>>();
var fluentEmailService = new FluentEmailService(fakeLogger, mockFluentEmail.Object);
// ACT
var sendResponse = await fluentEmailService.SendEmailAsync("Test Subject", "Some Path", It.IsAny<It.IsAnyType>(), "Some Recipient");
// ASSERT
Assert.NotNull(sendResponse);
Assert.True(sendResponse.Successful);
mockFluentEmail.Verify(f => f.To("Some Recipient"), Times.Once(), "Recipient should be set as: 'Some Recipient'.");
mockFluentEmail.Verify(f => f.Subject("Test Subject"), Times.Once, "Subject should be set as: 'Test Subject'.");
mockFluentEmail.Verify(f => f.UsingTemplateFromFile("Some Path", It.IsAny<It.IsAnyType>(), It.IsAny<bool>()), Times.Once, "Path should be set as: 'Some Path'.");
mockFluentEmail.Verify(f => f.SendAsync(null), Times.Once, "1 email should be sent.");
}
The test always fails because I get null as the sendResponse.
Can someone please tell me if I'm doing this correctly?
It is probably because of last function call SendAsync is not mocked and therefore returning null by default.
Since it is async call, it makes sense to use ReturnsAsync instead of Returns.
ReturnsAsync should also return an actual SendResponse:
//...
SendResponse expectedSendResponse = new SendResponse();
mockFluentEmail
.Setup(m => m.SendAsync(null))
.ReturnsAsync(expectedSendResponse);
//...

Unit test for API Controller in ASP.NET Core 3.1 returning a wrong status code

I'm writing a unit test for an API Controller performing delete action.
Here's the Delete Action
public IActionResult DeleteSubGenre(Guid subGenreId)
{
if (!_genreRepo.SubGenreExist(subGenreId))
{
return NotFound();
}
var genreObj = _genreRepo.SubGenre(subGenreId);
if (!_genreRepo.DeleteSubGenre(genreObj))
{
ModelState.AddModelError("", $"Something went wrong when deleting the record {genreObj.Name}");
return StatusCode(500, ModelState);
}
return NoContent();
}
The unit test for this action is written as
[Fact]
public void DeleteSubGenre_Returns_NoContentResult()
{
// Arrange
var subGenreRepositoryMock = new Mock<ISubGenreRepository>();
var subGenreIMapperMock = new MapperConfiguration(config =>
{
config.AddProfile(new MovieMapper());
});
var subGenreMapper = subGenreIMapperMock.CreateMapper();
SubGenresController subGenreApiController = new SubGenresController(subGenreRepositoryMock.Object, mapper: subGenreMapper);
var subGenreDto = new SubGenreDTO()
{
Name = "Adult Content",
DateCreated = DateTime.Parse("15 May 2015"),
Id = Guid.NewGuid(),
GenreId = Guid.NewGuid(),
Genres = new GenreDTO()
};
// Act
var subGenreResult = subGenreApiController.DeleteSubGenre(subGenreDto.Id);
var noContentResult = subGenreResult as NoContentResult;
// Assert
Assert.False(noContentResult.StatusCode is StatusCodes.Status204NoContent);
}
While debugging the test i noticed that subGenreResult was returning a status code of 404 instead of 204. I can seem to get a hang over it. I'll be glad to get plausible solution to this.
You have to setup your mock to drive the execution of your test case.
For example if you want to go through this line: if (!_genreRepo.SubGenreExist(subGenreId))
then you have to setup the following mock behaviour:
subGenreRepositoryMock.Setup(repo => repo.SubGenreExist(It.IsAny<Guid>)).Returns(true);
To reach this line: return NoContent(); you might need to setup the other two methods as well to drive your test case.

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 test a controller POST method which returns no data in response content in .NET Core 3.1?

i am new to integration tests. I have a controller method which adds a user to the database, as shown below:
[HttpPost]
public async Task<IActionResult> CreateUserAsync([FromBody] CreateUserRequest request)
{
try
{
var command = new CreateUserCommand
{
Login = request.Login,
Password = request.Password,
FirstName = request.FirstName,
LastName = request.LastName,
MailAddress = request.MailAddress,
TokenOwnerInformation = User
};
await CommandBus.SendAsync(command);
return Ok();
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
As you have noticed my method returns no information about the user which has been added to the database - it informs about the results of handling a certain request using the status codes. I have written an integration test to check is it working properly:
[Fact]
public async Task ShouldCreateUser()
{
// Arrange
var createUserRequest = new CreateUserRequest
{
Login = "testowyLogin",
Password = "testoweHaslo",
FirstName = "Aleksander",
LastName = "Kowalski",
MailAddress = "akowalski#onet.poczta.pl"
};
var serializedCreateUserRequest = SerializeObject(createUserRequest);
// Act
var response = await HttpClient.PostAsync(ApiRoutes.CreateUserAsyncRoute,
serializedCreateUserRequest);
// Assert
response
.StatusCode
.Should()
.Be(HttpStatusCode.OK);
}
I am not sure is it enough to assert just a status code of response returned from the server. I am confused because, i don't know, shall i attach to assert section code, which would get all the users and check does it contain created user for example. I don't even have any id of such a user because my application finds a new id for the user while adding him/her to the database. I also have no idea how to test methods like that:
[HttpGet("{userId:int}")]
public async Task<IActionResult> GetUserAsync([FromRoute] int userId)
{
try
{
var query = new GetUserQuery
{
UserId = userId,
TokenOwnerInformation = User
};
var user = await QueryBus
.SendAsync<GetUserQuery, UserDto>(query);
var result = user is null
? (IActionResult) NotFound(new
{
Message = (string) _stringLocalizer[UserConstants.UserNotFoundMessageKey]
})
: Ok(user);
return result;
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
I believe i should somehow create a user firstly in Arrange section, get it's id and then use it in Act section with the GetUserAsync method called with the request sent by HttpClient. Again the same problem - no information about user is returned, after creation (by the way - it is not returned, because of my CQRS design in whole application - commands return no information). Could you please explain me how to write such a tests properly? Have i missed anything? Thanks for any help.
This is how I do it:
var response = (CreatedResult) await _controller.Post(createUserRequest);
response.StatusCode.Should().Be(StatusCodes.Status201Created);
The second line above is not necessary, just there for illustration.
Also, your response it's better when you return a 201 (Created) instead of the 200(OK) on Post verbs, like:
return Created($"api/users/{user.id}", user);
To test NotFound's:
var result = (NotFoundObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status404NotFound);
The NotFoundObjectResult assumes you are returning something. If you are just responding with a 404 and no explanation, replace NotFoundObjectResult with a NotFoundResult.
And finally InternalServerErrors:
var result = (ObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status500InternalServerError);
You can use integrationFixture for that using this NuGet package. This is an AutoFixture alternative for integration tests.
The documented examples use Get calls but you can do other calls too. Logically, you should test for the status code (OkObjectResult means 200) value and the response (which could be an empty string, that is no problem at all).
Here is the documented example for a normal Get call.
[Fact]
public async Task GetTest()
{
// arrange
using (var fixture = new Fixture<Startup>())
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var controller = fixture.Create<SearchEngineController>();
// act
var response = await controller.GetNumberOfCharacters("Hoi");
// assert
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
Assert.Equal(8, ((OkObjectResult)response.Result).Value);
}
}
}
private void SetupStableServer(FluentMockServer fluentMockServer, string response)
{
fluentMockServer.Given(Request.Create().UsingGet())
.RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8)
.WithStatusCode(HttpStatusCode.OK));
}
In the example above, the controller is resolved using the DI described in your Startup class.
You can also do an actual REST call using using Refit. The application is self hosted inside your test.
using (var fixture = new RefitFixture<Startup, ISearchEngine>(RestService.For<ISearchEngine>))
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var refitClient = fixture.GetRefitClient();
var response = await refitClient.GetNumberOfCharacters("Hoi");
await response.EnsureSuccessStatusCodeAsync();
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
}
}

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

Categories

Resources