How write Unit test in Catch block in Xunit? - c#

In the following function, I want to test the case where an exception is thrown using XUnit. The test should verify that the excpetion is correctly thrown.
public IDictionary<string, Label> Build(string content)
{
try
{
var settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore
};
var contentStudioResponse = JsonConvert.DeserializeObject<ContentStudioResponse<CmsLabel>>(content, settings);
if (contentStudioResponse?.Items == null)
{
_logger.Warning("No records found in content studio response for label:({#content})", content);
return new Dictionary<string, Label>();
}
return contentStudioResponse.Items.ToDictionary(x => x.Key,
x => new Label
{
Value = x.DynamicProperties.MicroContentValue
}
);
}
catch (Exception e)
{
_logger.Error(e, "Failed to deserialize or build contentstudio response for label");
return new Dictionary<string, Label>();
}
}
Below is my solution which is not working:
[Fact]
public void Builder_ThrowsException()
{
string json_responsive_labels = "abcd";
var builder = new LabelBuilder(_testLogger).Build(json_responsive_labels);
Assert.Throws<Exception>(() => builder);
//var sut = new LabelBuilder(_testLogger);
//Should.Throw<Exception>(() => sut.Build(json_responsive_labels));
}

Have a read through of this. This explains step by step on how to test for an exception being thrown.
However, based on what you have written, the code won't throw an exception since at this point you're only logging your exception and then returning a Dictionary.
catch (Exception e)
{
_logger.Error(e, "Failed to deserialize or build contentstudio response for label");
return new Dictionary<string, Label>();
}
What you actually want to do is explicitly throw an exception like so:
catch (Exception e)
{
throw new Exception();
}
In doing so, your code will throw an exception which you can catch and test against.

Related

System.InvalidCastException: 'System.Linq.Expressions.PropertyExpression' to 'System.Linq.Expressions.MethodCallExpression'

I am trying to perform unit tests of a client that returns a task with a list of elements and iterates them, but what I have tried is not working for me.
IEnumerable<ExpenseNote> pendingExpenses = null;
try
{
pendingExpenses = await _expenseNoteService.GetPendingExpenseNotes().ConfigureAwait(false);
}
catch (Exception ex)
{
executionContext.Error($"Step 1-Final: Error when call \"getPendingExpenses()\". Detail: {ex.Message}", true);
throw;
}
if (!pendingExpenses.Any())
{
executionContext.Checkpoint("Step 1-Final: There are not pending expenses");
return;
}
int numApproveExpenses = 0;
executionContext.Checkpoint($"Step 2: Processing {pendingExpenses.Count()} pending expenses");
foreach (var expenseNote in pendingExpenses)
{
if (expenseNote.IsApprovable)
{
try
{
await _workflowsRRHHProxy.Approve(expenseNote.Id.ToString());
executionContext.Checkpoint($"Step 2.1: Expense with number {expenseNote.Number} has been approved ");
numApproveExpenses++;
}
catch (Exception ex)
{
executionContext.Error($"Step 2.1: Error when call \"Approve()\" with id: {expenseNote.Id} " +
$". Detail: {ex.Message}", false);
throw;
}
}
}
test example:
// ARRANGE
var mockExpenseNote = _mockFactory.CreateMock<DomainEntities.ExpenseNote>();
mockExpenseNote.Expects.One.Method(x => x.IsApprovable).WillReturn(true);
var expenseNote = mockExpenseNote.MockObject;
expenseNote.Id = Guid.NewGuid();
expenseNote.Number = "EX-APPROVE";
var expenseNotes = new List<DomainEntities.ExpenseNote>
{
expenseNote
};
Task<IEnumerable<DomainEntities.ExpenseNote>> task = Task.FromResult<IEnumerable<DomainEntities.ExpenseNote>>(expenseNotes);
var executionContext = _mockFactory.CreateMock<ITaskExecutionContext>();
executionContext.Expects.Between(1, 100).Method(method => method.Checkpoint("")).WithAnyArguments();
_expenseNoteService.Expects.One.Method(method => method.GetPendingExpenseNotes())
.WithAnyArguments()
.WillReturn(task);
var sut = GetServiceUnderTest();
// ACT
await sut.ProcessPendingExpenseNotes(executionContext.MockObject);
// ASSERT
_mockFactory.VerifyAllExpectationsHaveBeenMet();
but when i execute this test in " .WillReturn(task);" have this error:
System.InvalidCastException: Cannot cast object of type 'System.Linq.Expressions.PropertyExpression' to type 'System.Linq.Expressions.MethodCallExpression'..
Can Anyone Help me?
With mockExpenseNote.Expects.One.Method(x => x.IsApprovable).WillReturn(true); your lambda uses a property but you used the .Method() method so it expects you to use a method here (or call something else than .Method()).

Proper way to rethrow a JsonConverter exception

I have the following setup for deserializing some json:
parsedResponse = JsonConvert.DeserializeObject<T>(
json,
new JsonSerializerSettings
{
Error = (object sender, ErrorEventArgs args) =>
{
throw new MyParseException($"Parse error: {args.ErrorContext.Error.Message}");
},
Converters =
{
new MyItemConverter(),
new BoolConverter(),
new UnixDateTimeConverter(),
new NullableIntConverter(),
new UriConverter()
}
}
);
In one case, json has a bunch of null values (like "title" : null, etc) which causes a NullReferenceException in one of my converters. But throwing MyParseException in the error handler causes
System.InvalidOperationException: Current error context error is different to requested error.
I think I could do this instead:
try
{
parsedResponse = JsonConvert.DeserializeObject<T>(
json,
new JsonSerializerSettings
{
Converters =
{
new MyItemConverter(),
new BoolConverter(),
new UnixDateTimeConverter(),
new NullableIntConverter(),
new UriConverter()
}
}
);
}
catch (Exception ex)
{
throw new MyParseException($"Parse error: {ex.Message}");
}
But is there a better way? (Maybe something more similar to my original solution that doesn't cause the error context issue?)
Based on the Newtonsoft example here. I've ended up doing the following:
List<MyParseException> errors = new List<MyParseException>();
T parsedResponse = JsonConvert.DeserializeObject<T>(
json,
new JsonSerializerSettings
{
Error = (object sender, ErrorEventArgs args) =>
{
errors.Add(new MyParseException(String.Format("Parse error: {0}", args.ErrorContext.Error.Message), args.ErrorContext.Error));
args.ErrorContext.Handled = true;
},
Converters =
{
new MyItemConverter(),
new BoolConverter(),
new UnixDateTimeConverter(),
new NullableIntConverter(),
new UriConverter()
}
}
);
if (errors.Count == 1)
{
MyParseException firstException = errors[0];
firstException.Data["json"] = json;
throw firstException;
}
else if (errors.Count > 1)
{
AggregateException ex = new AggregateException("Unable to parse json. See innner exceptions and exception.Data[\"json\"] for details", errors);
ex.Data["json"] = json;
throw ex;
}

Data structure to use when gathering response from async calls

I am running this piece of code in my application.
public Task<BulkResponse<JObject>> GetRelatedObjectsAsync(IEnumerable<PrimaryObjectInfo> primaryObjectInfos)
{
var allSecondaries = new List<Tuple<int, List<JObject>>>();
var exceptionsDict = new ConcurrentDictionary<int, Exception>();
var relatedObjectsTasks = primaryObjectInfos.Select(async primaryObjectInfo =>
{
try
{
var secondaryObject = await objectManager.GetRelatedObjectsAsync(primaryObjectInfo);
allSecondaries.Add(Tuple.Create(primaryObjectInfo.Index, secondaryObject.ToList()));
}
catch (Exception ex)
{
exceptionsDict.TryAdd(primaryObjectInfo.Index, ex);
}
});
await Task.WhenAll(relatedObjectsTasks);
return ConvertToBulkResponse(allSecondaries, exceptionsDict);
}
When I run this code allSecondaries object sometimes returns valid list of results and sometimes the code ends up catching exceptions for the parallel threads I have for each primaryObjectInfo.
Async method objectManager.GetRelatedObjectsAsync() internally call 4-5 async functions and there are functions where parameters are passed by reference. (ref keyword)
Question:
Am I using the right data structure to consolidate the result from all parallel threads ?
If yes, what could be the reason I am getting different result every time?
You'd better split the execution from the results gathering:
public Task<BulkResponse<JObject>> GetRelatedObjectsAsync(
IEnumerable<PrimaryObjectInfo> primaryObjectInfos)
{
var relatedObjectsTasks = primaryObjectInfos
.Select(
async primaryObjectInfo =>
(primaryObjectInfo.Index,
await objectManager.GetRelatedObjectsAsync(primaryObjectInfo)))
.ToList();
try
{
await Task.WhenAll(relatedObjectsTasks);
}
catch
{
// observe each task's, exception
}
var allSecondaries = new List<(int index, List<JObject> related)>();
var exceptionsDict = new Dictionary<int, Exception>();
foreach (var relatedObjectsTask in relatedObjectsTasks)
{
try
{
allSecondaries.Add(relatedObjectsTask.Result);
}
catch (Exception ex)
{
exceptionsDict.Add(relatedObjectsTask.index, ex);
}
}
return ConvertToBulkResponse(allSecondaries, exceptionsDict);
}
You could look into each task's IsFaulted/Exception and IsCancelled properties instead of causing exceptions to be thrown:
foreach (var relatedObjectsTask in relatedObjectsTasks)
{
if (relatedObjectsTask.IsCancelled)
{
exceptionsDict.Add(
relatedObjectsTask.index,
new TaskCancelledException(relatedObjectsTask));
}
else if (relatedObjectsTask.IsFaulted)
{
exceptionsDict.TryAdd(relatedObjectsTask.index, ex);
}
else
{
allSecondaries.Add(relatedObjectsTask.Result);
}
}

Controller action that receives any data in xml json or form-urlencoded

I have a requirement to create a controller action that can accept any data in XML, JSON or form-urlencoded. However, I couldn't make it work.
If my action has a Dictionary<string, object> parameter, it works for JSON and XML data, but not for form-urlencoded. If my action has a FormDataCollection parameter, it works for form-urlencoded but not for JSON and XML.
[HttpPost]
public HttpResponseMessage Default(FormDataCollection data /*Dictionary<string,object> data*/)
{
try
{
if (data == null)
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = "The request body is empty" });
//var collection = GetCollection(data); //used when data=Dictionary<string,object>
var collection = data.ReadAsNameValueCollection();
var agent = new ScriptingAgentClient();
var parameters = ServiceAgentParameters.From(collection);
var result = agent.Run(parameters);
if (result.Error)
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = result.ErrorMessage, Exception = result.Exception });
if (result.Data != null && result.Data.Count == 1) //result.Data is byte[]
{
//TODO: use the right Serializer
var resultString = Encoding.UTF8.GetString(result.Data[0]);
var serializer = new JavaScriptSerializer();
var dict = serializer.Deserialize<Dictionary<string, string>>(resultString);
return Request.CreateResponse(HttpStatusCode.OK, dict);
}
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = "Unknown error" });
}
catch (Exception ex)
{
Logger.Error("Error handling request", ex);
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = ex.Unwrap().Message });
}
}
ScriptingAgentClient will handle the data, whatever it may be.
If this isn't possible, how can I create two actions with same route where one will handle XML/JSON and another will handle form-urlencoded?
Note that I'm using .net40 and I can't change that. Noteworthy to mention that this webapi is a self-hosted api that will run in a windows service.
I was able to make it work reading the Request instead of letting WebApi figuring it out:
[HttpPost]
public HttpResponseMessage Default()
{
try
{
NameValueCollection collection = Request.Content.IsFormData() ?
Request.Content.ReadAsFormDataAsync().Result :
GetCollection(Request.Content.ReadAsAsync<IDictionary<string, object>>().Result);
var parameters = ServiceAgentParameters.From(collection);
var agent = new ScriptingAgentClient();
var response = agent.Run(parameters);
if (response.Error)
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = response.ErrorMessage, Exception = response.Exception });
if (response.Data != null && response.Data.Count == 1) //result.Data is List<byte[]>
{
//TODO: use the right Serializer
var resultString = Encoding.UTF8.GetString(result.Data[0]);
var serializer = new JavaScriptSerializer();
var dict = serializer.Deserialize<Dictionary<string, string>>(resultString);
return Request.CreateResponse(HttpStatusCode.OK, dict);
}
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = "Unknown error" });
}
catch (Exception ex)
{
Logger.Error("Error handling request", ex);
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = ex.Unwrap().Message });
}
}

reduce complexity of service class

I am developing a web api using WCF Web Api preview 5. At the moment I have a resource class fully functional, how ever I noticed my methods inside this resouce are getting complex.
For example:
[WebInvoke(UriTemplate = "{EhrID}/PhysicalTest",Method="POST")]
public HttpResponseMessage<DTO.PhysicalTest> PostPhysicalTest(int EhrID, DTO.PhysicalTest PhysicalTestDTO)
{
var EHR = repository.FindById(EhrID);
var PhysicalTest = Mapper.Map<DTO.PhysicalTest, PhysicalTest>(PhysicalTestDTO);
if (PhysicalTest == null)
{
var response = CreateResponseForException("No object to store", HttpStatusCode.BadRequest);
throw new HttpResponseException(response);
}
try
{
if (EHR.PhysicalTests == null)
{
EHR.PhysicalTests = new List<PhysicalTest>();
}
PhysicalTest.CreationDate = DateTime.Now;
EHR.PhysicalTests.Add(PhysicalTest);
_unitOfWork.Commit();
return new HttpResponseMessage<DTO.PhysicalTest>(PhysicalTestDTO, HttpStatusCode.Created);
}
catch (Exception ex) {
var response = CreateResponseForException("Cannot create Physical Test", HttpStatusCode.InternalServerError);
throw new HttpResponseException(response);
}
}
As you may notice this method has the task of posting a new Physical Test, but it's actually validating my model too (I'm missing lots of validations still, property validations), which should not be this class concern. If there any approachable way to reduce the complexity of the methods inside de resource?
I would split it up into smaller more focused methods. I might also start using instance variables instead of passing all these arguments around, but for the sake of this post I've rewritten it without pushing stuff to instance variables.
[WebInvoke(UriTemplate = "{EhrID}/PhysicalTest",Method="POST")]
public HttpResponseMessage<DTO.PhysicalTest> PostPhysicalTest(int EhrID, DTO.PhysicalTest PhysicalTestDTO)
{
var EHR = repository.FindById(EhrID);
var PhysicalTest = Mapper.Map<DTO.PhysicalTest, PhysicalTest>(PhysicalTestDTO);
if (PhysicalTest == null)
{
var response = CreateResponseForException("No object to store", HttpStatusCode.BadRequest);
throw new HttpResponseException(response);
}
PostPhysicalTest(EHR, PhysicalTest);
return new HttpResponseMessage<DTO.PhysicalTest>(PhysicalTestDTO, HttpStatusCode.Created);
}
private void PostPhysicalTest(EHR ehr, PhysicalTest physicalTest)
{
try
{
CreatePhysicalTest(ehr, physicalTest);
}
catch (Exception ex) {
var response = CreateResponseForException("Cannot create Physical Test", HttpStatusCode.InternalServerError);
throw new HttpResponseException(response);
}
}
private void CreatePhysicalTest(EHR ehr, PhysicalTest physicalTest)
{
if (ehr.PhysicalTests == null)
{
ehr.PhysicalTests = new List<PhysicalTest>();
}
physicalTest.CreationDate = DateTime.Now;
ehr.PhysicalTests.Add(physicalTest);
_unitOfWork.Commit();
}

Categories