Nunit testing with action based method fails - c#

In my project we have created action based method like below...which work as expected in our project.
public async Task MyMethod(Action<bool> SuccessAction, Action<Exception> ErrorAction)
{
try
{
SuccessAction(false);
}
catch (Exception ex)
{
ErrorAction(ex);
}
}
Now, for testing the above method below is how i have written the test method using NUnit.
[Test]
public async Task MyFirstTest()
{
var myClass = new MyClass();
await myClass.MyMethod(
Result =>
{
Assert.IsTrue(Result);//as all are aware that this will throw an exception.
},
Error =>
{
Assert.Fail();
});
}
Now, my question is when ever there is an exception occured at MyFirstTest then that exception get caught at the MyMethod.
I am not sure why this is happening.
Can any one please provide an solution to handle this.
Please let me know if more information is required or my question is not clear.

Related

Exceptions in C# not behaving as expected

I am developing an ASP.NET Core 3.1 API for my React front end app.
My problem is that my exceptions are not trickling up through my object hierarchy as I expect. I think it might be due to some multi-threading issue, but I don't know enough about C# to be sure! I am learning on Pluralsight, but I'm currently studying networking, which is not going to help me with this!
The calling code is a SignalR Hub method, that looks like this:
public async Task<bool> UpdateProfile(object profileDto)
{
try
{
ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
_profile.UpdateProfile(profile);
return true;
}
catch (Exception e)
{
return false;
}
}
I would expect that any exceptions thrown or not handled in _profile.UpdateProfile(profile); would cause the exception block here to return false. My UpdateProfile() looks like this:
public void UpdateProfile(ProfileDTO profileDto)
{
_databaseService.ExecuteInTransaction(async session =>
{
// simulate an error
throw new Exception("Some exception");
});
}
...and my ExecuteInTransaction() looks like this:
public async void ExecuteInTransaction(Func<IClientSessionHandle, Task> databaseAction)
{
using var session = await Client.StartSessionAsync();
try
{
session.StartTransaction();
await databaseAction(session);
await session.CommitTransactionAsync();
}
catch(Exception e)
{
await session.AbortTransactionAsync();
throw e;
}
}
I would expect that the exception thrown in UpdateProfile() would trickle up to the catch block in ExecuteInTransaction() — which it does — but then further, I would expect this exception to trickle up to the Hub UpdateProfile() method. Instead, it ends up in the Throw() method on the ExceptionDispatchInfo class in the System.Runtime.ExceptionServices namespace.
Reading through the comments in this file makes me think it is a threading issue, but I don't know enough about how threading works yet in C#. Is it possible for the exceptions thrown in UpdateProfile() to make it up to the top level of my Hub UpdateProfile()? (just noticed they confusingly have the same name).
Your problem is the async void signature of ExecuteInTransaction.
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started
Source
What this means, if you're using ASP.NET Core where there is no SynchronizationContext, your exception will be thrown most likely on a threadpool thread if you didn't mess around with the TaskScheduler. If you're on older .NET framework code, it will be on the captured context but either way, what you know about exception handling does not apply here. You can catch these exceptions by subscribing to AppDomain.UnhandledException but no one wants to do that in maintainable code.
To fix this, change public async void ExecuteInTransaction to public async Task ExecuteInTransaction, change public void UpdateProfile to public async Task UpdateProfile and call it like so:
public async Task<bool> UpdateProfile(object profileDto)
{
try
{
ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
await _profile.UpdateProfile(profile);
return true;
}
catch (Exception e)
{
return false;
}
}
public async Task UpdateProfile(ProfileDTO profileDto)
{
await _databaseService.ExecuteInTransaction(async session =>
{
// simulate an error
throw new Exception("Some exception");
});
}

ASP.NET Core Exception middelware nesting exceptions for each layer

I created a global Exception handler middelware to catch all my custom exceptions.
When throwing an Exception in my DAL I expect that the middelware will catch it as the same type that it was thrown.
// API
[HttpGet]
[Route("api/users")]
public IActionResult Get(int id)
{
var user = _userService.GetById(id);
return Ok(user);
}
// Repository
public async Task<List<User>> GetById(int id)
{
throw new EntityNotFoundException("code", "message");
// .. return user
}
// Exception handler
public async Task Invoke(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex) // ex is of type JsonSerializationException
{
if (ex is EntityNotFoundException)
{
// Handle exception
}
}
}
In the above example the Exception is handled but is of type JsonSerializationException with an InnerException of type System.AggregateException that contains another InnerException with type EntityNotFoundException.
It seems that the Exception gets nested for each layer it gets passed along (DAL > Service > API). How can I avoid this so that I can catch the Exception as the original type?
The example you provided looks good but it lacks one important thing, which is single responsibility.
ASP.NET Core has a better approach, which is using exception filters, that can be registered globally too and can be written for each custom-exception and even for unhandled exceptions.
Sample:
public class EntityNotFoundExceptionFilter : IExceptionFilter
{
public EntityNotFoundExceptionFilter(// some dependencies that u want to inject)
{
...
}
public void OnException(ExceptionContext context)
{
if (!(context.Exception is EntityNotFoundException))
{
return;
}
context.ExceptionHandled = true;
context.Result = new NotFoundObjectResult // will produce 404 response, you can also set context.HttpContext.Response.StatusCode based on your exceptions statuscode and return an ObjectResult instead
{
context.Exception.Message
}
}
}
Now in your Startup.cs in the ConfigureServices(...) function add the following
public void ConfigureService(IServiceCollection services)
{
...
services.AddMvc(options =>
{
...
options.Filters.Add(typeof(EntityNotFoundExceptionFilter));
...
}
...
}
You will end up writing many filters but it is a cleaner approach and that is how the asp.net-core filterpipeline should be used + this will be working :)
I am not 100% sure why there are so many exceptions in your current implementation but my guess is that asp.net tries to return the exception and then fails to serialize it and stuff like that.
Edit:
I create a minimal example that can be found here. Just access the url via http://localhost:58741/api/some after cloning the project.

Exception handling in asp net core

I'm looking for some guidance on how to best handle exceptions in asp net core. Based on these docs from microsoft I've setup the UseStatusCodePagesWithRedirects middleware. This works for things such as 404's. However for API requests within my code that return exceptions this doesn't work. So following this doc I've setup an exception filter. This kinda works and this is where I'm looking for help.
I have the following code in a class library so it can be re used elsewhere. A mini wrapper for my api. I'm using Flurl to construct the request. My viewModelBuilder calls GetAll.
public async Task<List<TableDto>> GetAll(int branchId)
{
var result = await _baseUrl.AppendPathSegment("/v1/Table/GetAll").WithOAuthBearerToken(await _authenticationManager.GetToken()).GetJsonAsync<List<TableDto>>();
return result;
}
If GetAll throws an exception then the following exception filter comes into play.
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// ex.Call.Response.StatusCode // This works with the original exception but not here
if (context.HttpContext.Response.StatusCode == (int)HttpStatusCode.Forbidden)
{
// Always 200
}
}
}
I would like to do different things based on the exception status code. So for a 401 or 404 I would like to show an access denied screen or a login screen and then for other errors maybe just a general error screen. However context.HttpContext.Response.StatusCode is always 200.
Is this the right way to go about doing this? The error handing documentation suggests that middleware is better but I'm not sure if its referring to this use case as I could only get it working for 404s.
How do I get the correct status code in a exception filter?
I know that if I put a try catch around the original GetAll as below
try
{
var result = await _baseUrl.AppendPathSegment("/v1/Table/GetAll").WithOAuthBearerToken(await _authenticationManager.GetToken()).GetJsonAsync<List<TableDto>>();
return result;
}
catch (FlurlHttpException ex)
{
if (ex.Call.Response != null)
{
if (ex.Call.Response.StatusCode == HttpStatusCode.Forbidden)
{
throw new ForbiddenException();
}
}
}
then in the exception filter I can do the following
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.Exception is ForbiddenException)
{
}
}
}
and then I can do specific things for specific exceptions however thing means I have to try catch every call and voids the point of the global exception filter. Any advice would be appreciated.
Thanks
M
EDIT
Based on this answer here I could write a custom middleware and I quickly did that but I'm still stuck with the same problem of not being able to check for a 401 or 403 and only being able to get access to custom exceptions that have been throw. If I can access the status code then I think both solutions will work

C# unit testing for a NotFound via a GET

For some reason, I am having troubles making a unit test for the following in C#
[Route("api/Orders/{orderID:int}/Items")]
public OrderItemsDTO Get(int orderID)
{
if (_orderItemsService.Get(orderID).Items.Count() == 0)
{
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, String.Format("Order {0} not found.", orderID)));
}
return _orderItemsService.Get(orderID);
}
I have a unit test running correctly for an Async Add which is what I'm using to Post that I was trying to base my unit test off of with some tweaks I thought it would work, but it doesn't. the following is what I thought should work:
private OrderItemsController _testSubject;
private Mock<IOrderItemsService> _moqOrderItemsService = new Mock<IOrderItemsService>();
[TestInitialize]
public void TestInitialize()
{
_testSubject = new OrderItemsController(_moqOrderItemsService.Object);
}
[TestMethod]
[ExpectedException(typeof(HttpResponseException))]
public async Task ThrowHttpResponseExceptionWhenThereIsAValidationException()
{
_moqOrderItemsService.Setup(ois => ois.Get(It.IsAny<int>()))
.Throws(new ValidationException("test"));
try
{
_testSubject.Get(17);
}
catch(HttpResponseException ex)
{
Assert.IsNotNull(ex.Response);
Assert.AreEqual(HttpStatusCode.NotFound, ex.Response.StatusCode);
throw;
}
}
You set up order item service mock to throw ValidationException when it is called with any id. Then you are expecting controller to throw HttpResponseException which is not true - you will have same exception as you throw from service.
You should setup service to return some object (you didn't provide definition of service interface and types which it returns) with empty Items property:
_moqOrderItemsService.Setup(ois => ois.Get(It.IsAny<int>()))
.Returns(/* some object with empty Items property */);
Now in controller you will go to throwing HttpResponseException path.
Side note: why are you throwing exceptions instead of returning something like Content(HttpStatusCode.NotFound, "Message")? You even can create base controller with method IHttpActionResult NotFound(string message) which will do this for you.
I would suggest not testing the Web API controller, rather test the _orderItemsService and mock out whatever repository you are using in the service. You can assert that the OrderItemsDTO is not null.
Well according to Microsoft testing controllers is best practice: https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/unit-testing-controllers-in-web-api.

Unit testing WCF Faults

What's the best way to unit test expected faults from WCF services?
I am attempting to unit test a WCF service which is (correctly) throwing FaultExceptions for a certain reproducible error. The unit tests get an instance of the WCF client and call the applicable service method, which throws a FaultException.
All of that works as you would expect, but I am having difficulty unit testing this, because the fault causes the IDE to break when the error isn't caught in the service implementation. Because I am using faults, and not exceptions, I was expecting the IDE to serialize the exception and send it to the client, where it would raise an exception.
I do see that there is a configuration option to disable breaking for specific user-unhandled exceptions, but I was hoping somebody could point out a better way to achieve the same results, as this isn't easily doable in a team environment.
Here's some sample code of what the implementation currently looks like...
The unit test project has a service reference to my WCF service, and I have defined the interface as such:
[OperationContract(Name = "DoSomething")]
[FaultContract(typeof(EpicFail))]
ResponseObject DoSomething(RequestObject requestObject);
The fault is defined as such:
[DataContract]
public class EpicFail
{
public EpicFail(string action)
{
this.Reason = "Epic Fail";
this.Action = action;
}
[DataMember]
public string Reason
{
get;
set;
}
[DataMember]
public string Action
{
get;
set;
}
}
The code that calls the service looks vaguely like this:
[TestMethod()]
[ExpectedException(typeof(FaultException<EpicFail>))]
public void FaultTest_Fails_Epicly()
{
bool testPassed = false;
try
{
ResponseObject resp = GetServiceClient().DoSomething(req);
}
catch (FaultException<EpicFail>)
{
testPassed = true;
}
Assert.IsTrue(testPassed);
}
I edited the code to show that I am using the ExpectedException attribute and it doesn't seem to be having much effect on keeping the IDE/Debugger from breaking when the exception is thrown in the service.
You can always use ExpectedExceptionAttribute (in NUnit) to make sure this is the exception thrown. MSTest has similar concept as well.
[ExpectedException(typeof(MyException))]
void my_test()
{
// test
}
If you have some Mock verification to do, I would use try/catch block and verify in the catch and then throw the exception.
UPDATE
When you are using ExpectedException attribute, you are not supposed to catch the exception, instead you need to let the NUnit that runs your test to catch it.
If you need to verify special information in the exception then you catch the exception, verify the information and then rethrow:
[ExpectedException(typeof(MyException))]
void my_test()
{
try
{
// call the service
}
catch(MyException ex)
{
Assert.IsTrue(ex.Message.Contains("error code 200"));
throw ex;
}
}
mattv,
Why does this test has to access the service remotely? From what I see your code:
ResponseObject resp = GetServiceClient().DoSomething(req);
Is somehow getting a service client, and not a service instance itself. I'd advise to test the service concrete class directly for unit tests.
However, if you need this scenario, have you tried NOT CATCHING the exception and running the test? Does it give the same result?
And by the way, if you need to catch and rethrow use the following pattern:
try {
//Do something
}
catch(SomeException e) {
//Do something with e
throw
}

Categories

Resources