Unit test for web api action method responds while exception - c#

I was trying to write some unit tests for an web api action method while exception. So below my action method
[Route("{userName}/{searchCriteria}")]
[HttpGet]
public IHttpActionResult Events(string accountNumber, string searchCriteria)
{
try
{
bool isInputValid = _inputValidation.IsTrackingEventInputValid(accountNumber, searchCriteria);
if (isInputValid)
{
return OK ("my data");
}
else
{
throw new ArgumentException();
}
}
catch (ArgumentException ae)
{
return new ResponseMessageResult(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ExceptionHandlingMessages.InvalidArgumentException));
}
catch (Exception ex)
{
return new ResponseMessageResult(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ExceptionHandlingMessages.InternalServerError));
}
}
I want to check responds status code and responds messages while exception occurs. But problem is as soon as my execution hits ResponseMessageResult code it throws another ArgumentNullException saying Value cannot be null.Parameter name: request. Because of that control never returns to my unit test method.
My unit test method as
[TestMethod]
public void Events()
{
_mockInputValidation.Setup(x => x.IsTrackingEventInputValid(It.IsAny<string>(), It.IsAny<string>())).Returns(false);
//act
IHttpActionResult actionResult = _trackingEventController.Events(string.Empty, string.Empty);
//assert
}
I also tries putting [ExpectedException(type)] but not much helpful
how can I solve this

Refactor your code to try and avoid throwing exceptions in your actions. Let the exception handler/filter handle them (cross-cutting concerns). Your original issue could have happened if you did not provide a proper request message for unit test.
[Route("{userName}/{searchCriteria}")]
[HttpGet]
public IHttpActionResult Events(string accountNumber, string searchCriteria) {
bool isInputValid = _inputValidation.IsTrackingEventInputValid(accountNumber, searchCriteria);
if (isInputValid) {
return Ok("my data");
} else {
return BadRequest(ExceptionHandlingMessages.InvalidArgumentException);
}
}
And then for the particular test case
[TestMethod]
public void IsTrackingEventInputValid_When_False_Should_Return_BadRequest() {
//Arrange
_mockInputValidation.Setup(x => x.IsTrackingEventInputValid(It.IsAny<string>(), It.IsAny<string>())).Returns(false);
var expected = ExceptionHandlingMessages.InvalidArgumentException;
//Act
var actionResult = _trackingEventController.Events(string.Empty, string.Empty) as BadRequestErrorMessageResult;
//Assert
Assert.IsNotNull(actionResult);
Assert.AreEqual(expected, actionResult.Message);
}

Related

How to mock setup for interface Ilogger's Error method?

My Controller's method
public async Task<IActionResult> GetPathData([FromODataUri] string uid)
{
try
{
if (!Guid.TryParse(uid, out Guid requestTypeGuid))
{
throw new ArgumentException($"{nameof(requestTypeGuid)} is null");
}
...
return Ok(response);
}
catch (Exception ex)
{
_log.Error(ex, ex.Message);
return BadRequest(ex.Message);
}
}
my mock setup
public class SomeControllerTest
{
private MockRepository mockRepository;
private Mock<ILog> mockLog;
public SomeControllerTest()
{
this.mockRepository = new MockRepository(MockBehavior.Strict);
this.mockLog = this.mockRepository.Create<ILog>();
}
private SomeController CreateSomeController()
{
return new SomeController(this.mockLog.Object);
}
my unit test case
[Fact]
public async Task GetPathData_IfBlock_ArgumentException()
{
// Arrange
var someController = this.CreateSomeController();
mockLog.Setup(x => x.Error(It.IsAny<string>())); //I tried this
//Act
var result = await someController.GetPathData("2");
//Assert
var ex = Assert.Throws<ArgumentException>(() => result);
Assert.Equal("requestTypeGuid is null", ex.Message);
}
getting error : Message:
Moq.MockException : ILog.Error(System.ArgumentException:
requestTypeGuid is null
at TMo.MWav.API.Controllers.SomeController.GetPathData(String uid) "requestTypeGuid is null") invocation failed with mock behavior
Strict.
All invocations on the mock must have a corresponding setup.
If you use MockBehavior.Strict, you should set up ALL invocations.
Fou your use case:
public async Task<IActionResult> GetPathData([FromODataUri] string uid)
{
// ...
catch (Exception ex)
{
_log.Error(ex, ex.Message);
// invoke ILog.Error with two parameters: `Exception` and `string`
}
// ...
}
, the test should be set up like this:
public async Task GetPathData_IfBlock_ArgumentException()
{
// ...
mockLog.Setup(x => x.Error(It.IsAny<Exception>(), It.IsAny<string>()));
// ...
}
Maybe you can use the test to check your method behavior:
[Fact]
public async Task GetPathData_IfBlock_ArgumentException()
{
// Arrange
var expectedMsg = "requestTypeGuid is null";
var someController = this.CreateSomeController();
mockLog.Setup(x => x.Error(It.IsAny<Exception>(), It.IsAny<string>()));
//Act
var result = await someController.GetPathData("2");
//Assert
Assert.IsType<BadRequestObjectResult>(result);
Assert.Equal(expectedMsg, (result as BadRequestObjectResult)?.Value);
mockLog.Verify(
x => x.Error(It.IsAny<ArgumentException>(), expectedMsg),
Times.Once);
}

How do I write a unit test for checking a bad request in a conditional statement in C# ASP.NET?

I have a controller with a simple conditional statement that checks a value and then returns a BadRequest should that value not match what is expected.
public async Task<IActionResult> SendCarDetails(string carValue)
{
try
{
if (carValue== "0")
{
return BadRequest("Car value cannot be 0");
}
}
catch (Exception ex)
{
return StatusCode(500);
}
}
To test this simple condition I wrote a unit test, although it works I feel that it's a little disconnected from the above method and therefore is not sufficient to qualify as a test. This defines the value as being 0 and then checks it, I then create a new BadRequestResult and should that response match 400 then the test is passed.
[TestMethod]
public void SendCarDetails_Returns_400_When_Zero()
{
string paymentValue = "0";
if (paymentValue == "0")
{
var response = new BadRequestResult();
Assert.AreEqual(StatusCodes.Status400BadRequest, response.StatusCode);
}
}
Can someone guide me on writing a unit test for my controller condition or is my approach above accurate?
The shown test above is disconnected from the controller action to be tested.
Ideally you want to create an instance of the subject under test and invoke the target member to assert the expected behavior
For example
[TestMethod]
public async Task SendCarDetails_Returns_400_When_Zero() {
//Arrange
MyController subject = new MyController();
string paymentValue = "0";
//Act
IActionResult result = await subject.SendCarDetails(paymentValue);
BadRequestObjectResult actual = result as BadRequestObjectResult ;
//Assert
Assert.IsNotNull(actual);
}

ASP.NET Core 3.0 WebAPI - why do I still receive InternalServerError when exception is handled in the Controller Method?

When I catch an exception in a controller, the client side still receives an InternalServerError, how can I resolve this?
I have my own 'Result' class, which I can set to failed, and populate with a user friendly error message, I want to catch exceptions in specific controller methods, and send back my failed 'Result' class instead of the code 500 InternalServerError.
Thanks for the help!
[HttpGet]
[Route(Server_DevTestApiEndpoints.INTERNAL_SERVER_EXCEPTION_RESULT_TEST)]
public Result GetInternalServerExceptionTest()
{
try
{
throw new Exception();
return new Result();
}
catch (Exception e)
{
return new Result(e);
}
}
It will be better from architectural standpoint to use an ExceptionFilterAttribute, that will be responsible for catching all errors in your controller actions:
public class LogExceptionFilterAttribute : ExceptionFilterAttribute
{
public override async Task OnExceptionAsync(ExceptionContext context)
{
//log your exception;
context.Result = new ObjectResult("your custom message")
{
StatusCode = StatusCodes.Status200OK
};
}
}
and just decorate your action or entire controller:
[HttpGet]
[LogExceptionFilter]
[Route(Server_DevTestApiEndpoints.INTERNAL_SERVER_EXCEPTION_RESULT_TEST)]
public Result GetInternalServerExceptionTest()
{
//your action logic
}

How to test if Api returns a specific message after the exception is caught in the try/catch

I am writing a test method in xunit. So when a user fails to enroll, a specific message is returned in the catch block. In the test method, I want to check if the right message is returned. But when I run, I get this message instead:
EnrollUser failed with HTTP response code:BadRequest.
If anyone can help me find out, if it possible, how to retrieve the message I want.
Below is the code.
public async Task<IHttpActionResult> EnrollUser([FromUri]int id, int userId, int?
organizationId = null)
{
try
{
if (organizationId.HasValue)
{
Do something.....
}
else
{
Do something.....
}
return Ok();
}
catch (Exception e)
{
var errorMsg = $"User enrollment failed for userId:{userId}, serviceId:{id}.";
return (IHttpActionResult)BadRequest(errorMsg);
}
}
[Fact]
public async Task EnrollUser_Should_Return_Failure_Message()
{
const int userId = 2310;
const int serviceId = 9;
const string expectedMessage = "User enrollment failed for userId:{userId}, serviceId:{id}.";
Func<Task> act = () => EnrollUser(userId, serviceId);
var actualException = await Assert.ThrowsAsync<Exception>(act);
Assert.Equal(expectedMessage, actualException.Message);
}
The Exception is handled in the EnrollUser method so this method will never throw an exception, It will return BadRequest object instead that you can use to compare the message.
var result = await act;
Assert.Equal(expectedMessage, act.Request);

Unit testing code using Flurl GetResponseJson<TError>()

I'm trying to unit test a controller that is catching a FlurlHttpException and calling GetResponseJson<TError>() to get the error message in the catch block. I attempted to mock the exception, but the Call property does not allow me set the Settings. When the unit test runs it fails because there isn't a JsonSerializer in the settings. How do I setup this test?
Here's my current attempt that does not work:
Controller
[Route]
public async Task<IHttpActionResult> Post(SomeModel model)
{
try
{
var id = await _serviceClient.Create(model);
return Ok(new { id });
}
catch (FlurlHttpException ex)
{
if (ex.Call.HttpStatus == HttpStatusCode.BadRequest)
return BadRequest(ex.GetResponseJson<BadRequestError>().Message);
throw;
}
}
Unit Test
[TestMethod]
public async Task Post_ServiceClientBadRequest_ShouldReturnBadRequestWithMessage()
{
//Arrange
string errorMessage = "A bad request";
string jsonErrorResponse = JsonConvert.SerializeObject(new BadRequestError { Message = errorMessage });
var badRequestCall = new HttpCall
{
Response = new HttpResponseMessage(HttpStatusCode.BadRequest),
ErrorResponseBody = jsonErrorResponse
//This would work, but Settings has a private set, so I can't
//,Settings = new FlurlHttpSettings { JsonSerializer = new NewtonsoftJsonSerializer(new JsonSerializerSettings()) }
};
_mockServiceClient
.Setup(client => client.create(It.IsAny<SomeModel>()))
.ThrowsAsync(new FlurlHttpException(badRequestCall, "exception", new Exception()));
//Act
var result = await _controller.Post(new SomeModel());
var response = result as BadRequestErrorMessageResult;
//Assert
Assert.IsNotNull(response);
Assert.AreEqual(errorMessage, response.Message);
}
If you are encapsulating the usage of Flurl within your ServiceClient object, then I think catching FlurlException, extracting Message, and returning a more appropriate exception should also be encapsulated in that service. This will make your controller much easier to test.

Categories

Resources