I'm currently trying to test the following code in an application that makes use of the Microsoft Bot Framework.
public async Task ResumeAfterCalculation_v2FormDialog(IDialogContext context, IAwaitable<Calculation_v2Form> result)
{
try
{
var extractedCalculationForm = await result;
//Removed additional code
}
catch (FormCanceledException ex)
{
var reply = "You have canceled the operation.";
await _chat.PostAsync(context, reply);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
context.Done<object>(null);
}
}
When a user types 'quit' to the bot the 'await result' code throws a FormCanceledException and the code quits the form.
When creating a test I implemented a class to mock the IAwaitable:
public class TaskAwaiterHelper<T> : IAwaiter<T>, IAwaitable<T>
{
public Task<T> Task { get; }
public TaskAwaiterHelper(T obj)
{
this.Task = System.Threading.Tasks.Task.FromResult(obj);
}
public TaskAwaiterHelper(Task<T> task)
{
this.Task = task;
}
public bool IsCompleted { get { return Task.IsCompleted; } }
public void OnCompleted(Action action)
{
SynchronizationContext context = SynchronizationContext.Current;
TaskScheduler scheduler = context == null ? TaskScheduler.Current
: TaskScheduler.FromCurrentSynchronizationContext();
Task.ContinueWith(ignored => action(), scheduler);
}
public T GetResult()
{
return Task.Result;
}
public IAwaiter<T> GetAwaiter()
{
return this;
}
}
I then created the following test:
[Fact]
public async Task ResumeAfterCalculation_v2FormDialog_WasCancelled_ThenCallsDone()
{
//Arrange
var chat = new Mock<IChatHelper>();
var calculationApi = new Mock<ICalculationApi>();
var dialogContextMock = new Mock<IDialogContext>();
var rootLuisDialog = new RootLuisDialog(chat.Object, calculationApi.Object);
var taskAwaiter = new TaskAwaiterHelper<Calculation_v2Form>(new Task<Calculation_v2Form>(() =>
{
throw new FormCanceledException("Error created for test test", null);
}));
taskAwaiter.Task.Start();
//Act
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, taskAwaiter);
//Assert
chat.Verify(c => c.PostAsync(dialogContextMock.Object, "You have canceled the operation."), Times.Once());
dialogContextMock.Verify(t => t.Done<object>(null), Times.Once());
}
Now whatever I try to do I the exception that's being thrown in the IAwaitable is being wrapped in an AggregateException, so we always end up in the catch (Exception ex) instead of the desired catch (FormCanceledException ex)
Is there a way to make a Task throw a specific Exception instead of an AggregateException (I mean there should be as the bot framework itself seems to be able to do it).
I just found the answer, I basically created a new class:
public class ExceptionThrower : IAwaitable<Calculation_v2Form>
{
public IAwaiter<Calculation_v2Form> GetAwaiter()
{
throw new FormCanceledException("Error created for test test", null);
}
}
And just provided this to the method:
var exceptionThrower = new ExceptionThrower();
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, exceptionThrower);
Related
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);
}
I have a try/catch block in both my application and unit test. I'm trying to create a test that catches an exception being thrown in my actor. When debugging through the code I get the exception being thrown but in my test I never receive the exception.
Application:
public class FooActor : ReceiveActor {
private readonly IFooService fooService;
private readonly IChildActorFactory childCreatorFactory;
private IActorRef barActor;
public FooActor(IFooService fooService, IChildActorFactory childCreatorFactory) {
this.fooService = fooService;
this.childCreatorFactory = childCreatorFactory;
ReceiveAsync<bool>(async (x) => await StartAsync(x).ConfigureAwait(false));
}
protected override void PreStart() {
barActor = childCreatorFactory.Create<BarActor>(Context, "BarActor");
}
public async Task StartAsync(bool start) {
try {
if (start) {
var fooInformation = await fooService.GetInformationAsync().ConfigureAwait(false);
if (fooInformation != null) {
barActor.Tell(fooInformation);
}
}
} catch (Exception exception) {
throw new Exception($"Unhandled exception. Actor {Self.Path.Name};", exception);
}
}
}
Test:
[Fact]
public void StartAsync_ThrowsException_ExceptionThrown() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
Sys.UseAutofac(mock.Container);
var mockChildActorFactory = mock.Mock<IChildActorFactory>();
var mockBarService = mock.Mock<IBarService>();
mockBarService.Setup(x => x.GetInformationAsync()).Throws(new Exception());
var props = Props.Create(() => new FooActor(mockBarService.Object, mockChildActorFactory.Object));
var fooActorName = "FooActor";
var fooActor = new TestActorRef<FooActor>(Sys, props, null, fooActorName);
try {
// Act
fooActor.Receive(true);
} catch (Exception exception) {
// Assert
Assert.Equal($"Unhandled Exception. Actor { fooActorName }.", exception.Message);
}
}
}
The problem is in the async/await operators in ReceiveAsync method:
ReceiveAsync<bool>(async (x) => await StartAsync(x).ConfigureAwait(false));
when execution context reached the await operation it just start Task at thread pool(simplified) and returns to the caller. I.e. when
fooActor.Receive(true);
completed, the actual task with StartAsync(x) may not be started yet. So when actual exception is thrown your test is already finished without any exception.
For some part of my system I need to add retry logic for reading from the database. I have a number of repositories with async and sync read methods that I can't change. I found a simple solution - interception of all read methods with AsyncInterceptor and add retry read policy with Polly when database exception caught. Polly retries reading with some intervals.
Interceptor code:
public class RetriableReadAsyncInterceptor : IAsyncInterceptor
{
public void InterceptSynchronous(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptSync(invocation);
}
public void InterceptAsynchronous(IInvocation invocation)
{
throw new NotImplementedException();
}
public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptAsync<TResult>(invocation);
}
private IEnumerable<TimeSpan> RetryIntervals =>
new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(15)
};
private object InternalInterceptSync(IInvocation invocation)
{
return Policy
.Handle<DatabaseException>()
.WaitAndRetry(RetryIntervals, (exception, timeSpan) =>
{
Console.WriteLine($"Exception {timeSpan}");
})
.Execute(() =>
{
invocation.Proceed();
return invocation.ReturnValue;
});
}
private async Task<TResult> InternalInterceptAsync<TResult>(IInvocation invocation)
{
return await Policy
.Handle<DatabaseException>()
.WaitAndRetryAsync(RetryIntervals, (exception, timeSpan) =>
{
Console.WriteLine($"Exception {timeSpan}");
})
.ExecuteAsync(async () =>
{
invocation.Proceed();
var task = (Task<TResult>)invocation.ReturnValue;
return await task;
});
}
}
Repository code:
public class Repository : IRepository
{
private int _exceptionsCoutner;
public Entity GetById(int id)
{
if (_exceptionsCoutner <= 2)
{
_exceptionsCoutner++;
throw new DatabaseException();
}
//read from db
return new Entity {Id = id};
}
public async Task<Entity> GetByIdAsync(int id)
{
if (_exceptionsCoutner <= 2)
{
_exceptionsCoutner++;
throw new DatabaseException();
}
//read from db
return await Task.FromResult(new Entity { Id = id });
}
}
Sync version of GetById works as expected (retries with intervals):
Exception 00:00:01
Exception 00:00:05
Exception 00:00:10
Async version of GetById retries but not waits for time interval elapsed:
Exception 00:00:01
Exception 00:00:01
Exception 00:00:01
I can't understand where is the problem. If you have any thoughts - please share.
Full example can be found here.
It was a kind of 'chicken and egg' problem which can be solved now with newer version of Castle.Core (I tried version 4.4.0) leveraging the invocation.CaptureProceedInfo method:
private Task<TResult> InternalInterceptAsync<TResult>(IInvocation invocation)
{
var capture = invocation.CaptureProceedInfo();
return Policy
.Handle<DatabaseException>()
.WaitAndRetryAsync(RetryIntervals, (exception, timeSpan) =>
{
Console.WriteLine($"Exception {timeSpan}");
})
.ExecuteAsync(async () =>
{
capture.Invoke();
var task = (Task<TResult>)invocation.ReturnValue;
return await task;
});
}
OK, here is my naive implementation of retry:
public class Retry
{
public static async Task<TResult> DoAsync<TResult, TException>(
Func<Task<TResult>> action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
where TException : Exception
{
TException exception = null;
var startDateTime = DateTime.UtcNow;
for (var attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
return await action().ConfigureAwait(false);
}
catch (TException ex)
{
exception = ex;
Console.WriteLine($"Exception {DateTime.UtcNow - startDateTime}");
await Task.Delay(retryInterval); //doesnt work
//Thread.Sleep(retryInterval); works!
}
}
throw exception;
}
}
And interceptor:
private async Task<TResult> InternalInterceptAsync<TResult>(IInvocation invocation)
{
return await Retry.DoAsync<TResult, DatabaseException>(async () =>
{
invocation.Proceed();
var task = (Task<TResult>) invocation.ReturnValue;
return await task.ConfigureAwait(false);
},
TimeSpan.FromSeconds(3),
4);
}
Implementation with blocking Tread.Sleep works well, but with Task.Delay dont.
I have written a very simple WebApiClient extending HttpClient. The code is following. The main reason to do that was to throw MyOwnWebApiException when httpResponse.IsSuccessStatusCode is false.
public class WebApiClient : HttpClient
{
public WebApiClient(string apiBaseUrl)
{
this.BaseAddress = new Uri(apiBaseUrl);
this.DefaultRequestHeaders.Accept.Clear();
}
public void AddAcceptHeaders(MediaTypeWithQualityHeaderValue header)
{
this.DefaultRequestHeaders.Accept.Add(header);
}
public async Task<string> DoPost(string endPoint, Object dataToPost)
{
HttpResponseMessage httpResponse = await ((HttpClient)this).PostAsJsonAsync(endPoint, dataToPost);
if (httpResponse.IsSuccessStatusCode)
{
string rawResponse = await httpResponse.Content.ReadAsStringAsync();
return rawResponse;
}
else
{
string rawException = await httpResponse.Content.ReadAsStringAsync();
MyOwnWebApiErrorResponse exception =
JsonConvert.DeserializeObject<MyOwnApiErrorResponse>(rawException, GetJsonSerializerSettings());
throw new MyOwnWebApiException (exception.StatusCode,exception.Message,exception.DeveloperMessage,exception.HelpLink);
}
}
#region "Private Methods"
private static JsonSerializerSettings GetJsonSerializerSettings()
{
// Serializer Settings
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
return settings;
}
#endregion
Following is the code of the class using WebApiClient.
class TestWebApiClient
{
private WebApiClient _client;
public ComputationProcessesWebApiClient()
{
_client = new WebApiClient("http://test.api/");
_client.AddAcceptHeaders(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void GetData(string dataFor)
{
try
{
DataRequest request = new DataRequest();
request.dataFor = dataFor;
**// THIS LINE IS THROWING AGGREGATEEXCEPTION--- **I WANT MyOwnException ****
string response = _client.DoPost("GetData", request).Result; // Use the End Point here ....
}
catch (MyOwnWebApiException exception)
{
//Handle exception here
}
}
}
Question
In the TestWebApiClient class, i dont want to catch AggregateException, rather i want to keep it more elegent and catch MyOwnWebApiException, but the problem is the line ** _client.DoPost("GetData", request).Result** throws an AggregateException if something goes wrong from the WebApi. How to change the code so that from TestWebApiClient i only have to catch MyOwnException ??
This is as a result of synchronously waiting for your task. If you stay async and await your task instead, you'll find that your actual Exception is the one that is caught.
Compare the following below:
void Main()
{
TryCatch();
TryCatchAsync();
}
void TryCatch()
{
try
{
ThrowAnError().Wait();
}
catch(Exception ex)
{
//AggregateException
Console.WriteLine(ex);
}
}
async Task TryCatchAsync()
{
try
{
await ThrowAnError();
}
catch(Exception ex)
{
//MyException
Console.WriteLine(ex);
}
}
async Task ThrowAnError()
{
await Task.Yield();
throw new MyException();
}
public class MyException:Exception{};
Top hint for async/await? It's async/await all the way down. The moment you .Wait() or .Result on a Task, things start to get messy.
I have some logic for Task and Task<T>.
Is there any way to avoid duplicating code ?
My current code is following:
public async Task<SocialNetworkUserInfo> GetMe()
{
return await WrapException(() => new SocialNetworkUserInfo());
}
public async Task AuthenticateAsync()
{
await WrapException(() => _facebook.Authenticate());
}
public async Task<T> WrapException<T>(Func<Task<T>> task)
{
try
{
return await task();
}
catch (FacebookNoInternetException ex)
{
throw new NoResponseException(ex.Message, ex, true);
}
catch (FacebookException ex)
{
throw new SocialNetworkException("Social network call failed", ex);
}
}
public async Task WrapException(Func<Task> task)
{
try
{
await task();
}
catch (FacebookNoInternetException ex)
{
throw new NoResponseException(ex.Message, ex, true);
}
catch (FacebookException ex)
{
throw new SocialNetworkException("Social network call failed", ex);
}
}
You can make the Task overload call the other one, and return a dummy value.
public async Task WrapException(Func<Task> task)
{
await WrapException<object>(async () => {
await task();
return null;
});
}
Or, since the async keyword is unnecessary here:
public Task WrapException(Func<Task> task)
{
return WrapException<object>(async () => {
await task();
return null;
});
}
Assuming that Func does not itself throw, the following would work.
public async Task<T> WrapException<T>(Func<Task<T>> task)
{
var actualTask = task();
await WrapException((Task)actualTask);
return actualTask.Result;
}
We know that Result won't block or throw since WrapException ensured it ran to completion.