How to make C# unit test fail if child threads throw exceptions? - c#

I have a multi-threaded class which throws an exception in a child thread:
public class TestClass
{
private Thread theThread;
private string failString = string.Empty;
private CancellationTokenSource cancelToken;
public void OnStart()
{
cancelToken = new CancellationTokenSource();
theThread = new Thread(() => ThreadOperation(cancelToken.Token));
theThread.Start();
}
private void ThreadOperation(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
if(failString[0] == ',')
{
Console.WriteLine("Foo");
}
Thread.Sleep(10);
}
}
public void OnStop()
{
cancelToken.Cancel();
theThread.Join();
}
}
And I want to write a unit test that catches the exception and fails in that case.
But a simple unit test trying to catch the exception fails only in Debug:
[TestMethod]
public void TestException()
{
TestClass testClass = new TestClass();
testClass.OnStart();
Thread.Sleep(1000);
testClass.OnStop();
}
If I just run this test (without debugging), it passes successfully (which is not the behavior I want to achieve).
I've tried this but it doesn't fail either:
[TestMethod]
public void TestException()
{
try
{
TestClass testClass = new TestClass();
testClass.OnStart();
Thread.Sleep(1000);
testClass.OnStop();
}
catch(Exception)
{
Assert.Fail();
}
}
Is there any way to catch the exceptions of all threads (including children) and make the test fail in that case without rewriting the original class?

Normally unhandled exceptions should cause the application to terminate. There is an unhandled exception handler that can be used as a last chance to catch exception that occurs on worker threads. But this is a global exception handler so not suitable for changing by individual tests, and the unit test framework might already have attached a handler to this.
I would suggest changing from using Thread to using a Task with the LongRunning flag. This automatically captures exceptions and you can let OnStop to either return the task, or Wait on the task. The later should throw an AggregateException if any exception was encountered.
An alternative would be to manually insert a try/catch in ThreadOperation and save the exception so it can be reported back when OnStop is called.

Related

C#: calling [async] method without [await] will not catch its thrown exception?

I'm having this code snippet:
class Program
{
public static async Task ProcessAsync(string s)
{
Console.WriteLine("call function");
if (s == null)
{
Console.WriteLine("throw");
throw new ArgumentNullException("s");
}
Console.WriteLine("print");
await Task.Run(() => Console.WriteLine(s));
Console.WriteLine("end");
}
public static void Main(string[] args)
{
try
{
ProcessAsync(null);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
}
It runs and prints:
call function
throw
Ok, and exception is thrown, but the main function's try/catch is not able to catch the exception, if I remove the try/catch, main doesn't report unhandled exception either. This is very weird, I googled and it says there's trap in [await] but doesn't explain how and why.
So my question, why here the exception is not caught, what's the pitfalls of using await?
Thanks a lot.
Within an async method, any exceptions are caught by the runtime and placed on the returned Task. If your code ignores the Task returned by an async method, then it will not observe those exceptions. Most tasks should be awaited at some point to observe their results (including exceptions).
The easiest solution is to make your Main asynchronous:
public static async Task Main(string[] args)
{
try
{
await ProcessAsync(null);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}

Catching an exception thrown from the click handler of a ContentDialog outside of ShowAsync()

Currently if I throw an exception somewhere down the call stack from the click handler it will crash the application. Is there a way to allow the exception out of the ContentDialog.ShowAsync()?
public async Task<bool> ShowLoginDialogAsync(LogInType loginType) {
var loginDialog = new LoginDialog(loginType);
try {
await loginDialog.ShowAsync(); <-- Exception thrown in click handler will crash the app
}
catch { } <-- I'd like to cach login exceptions here rather than be limited the ContentDialog return result
return loginDialog.Result;
}
public sealed partial class LoginDialog {
private async void OkClicked(ContentDialog contentDialog, ContentDialogButtonClickEventArgs args) {
await Validate(); <-- last chance to catch an exception or crash?
}
}
The OkClicked code doesn't run inside the loginDialog.ShowAsync(), it runs independently. You have to wrap the call to Validate in a try/catch if you want to get the exception from it, or it will just propagate to the context and, uncaught, crash the application.
I've currently decided to use the following strategy in several places to work with converting our WinForms/WPF app to UWP. I wouldn't normally do this and I may choose to factor it out later, but this code allows me to propagate exceptions out of the ContentDialog and abide the async/await pattern:
public sealed partial class LoginDialog {
public Exception Exception { get; private set; }
private async void OkClicked(ContentDialog contentDialog, ContentDialogButtonClickEventArgs args) {
try {
await Validate();
}
catch (Exception e) {
Exception = e;
}
}
}
public async Task<bool> ShowLoginDialogAsync(LogInType loginType) {
var loginDialog = new LoginDialog(loginType);
await loginDialog.ShowAsync();
switch (loginDialog.Exception) {
case null:
break;
default:
throw loginDialog.Exception;
}
return loginDialog.Result;
}

Handle exception in consumer queue

public class ProducerConsumerQueue
{
public void EnqueueTask(MyTask task)
{
}
void Work()
{
while (true)
{
try
{
// my task goes here
Thread.Sleep(2000);
}
catch(Exception ex)
{
Log(ex);
}
}
}
}
Producer:
public void Add()
{
MyTask task = new MyTask();
new ProducerConsumerQueue().EnqueueTask(task);
}
I'm in .NET 3.5.
Add() method will be called by my API users. In the example above, inside the method, void work(), I'm catching the exception and logging there.
But instead of that, I would like to catch and rethrow the exception to the user. Sametime, the permanent thread that run inside the while loop, should recover from the exception by continue to the next task in the queue. My short question is - How will I throw exception that happen inside void work(), but still the consumer stay alive for next task in the queue.
Continuing our discussion from the comments, you could possibly do something like collecting all the exceptions occurring when executing a queue of tasks (however you need to execute the queue on cycles) and then throwing it back to the caller.
So something like:
public void ExecuteAllTasks()
{
var exceptions = new List<Exception>();
IEnumerable<MyTask> tasks = GetQueuedTasks(); // get all tasks (or possibly pass them to the method) ...
foreach (MyTask task in tasks)
{
try
{
// execute your tasks here ...
}
catch (Exception ex)
{
// collect all the exceptions
exceptions.Add(ex);
}
}
// throw all the errors at once
if (exceptions.Any())
throw new AggregateException(_exceptions);
}
I hope this helps.
You need to establish some sort of communications between your consumer threads and the main thread. When a consumer encounters an exception, it should notify the main thread and move on to the next task.
Since you're using Winforms, the easiest way to inform the main thread is to use Invoke. See the following question for an example.
Introduce a callback which is invoked when the task has completed:
public interface ICompletionState
{
public ITask Task { get; set; }
public Exception Exception { get; set; }
}
public class CompletionState : ICompletionState
{
public ITask Task { get; set; }
public Exception Exception { get; set; }
public Action<ICompletionState> Callback { get; set; }
}
public class ProducerConsumerQueue
{
ConcurrentQueue<CompletionState> _tasks = new ConcurrentQueue<CompletionState>();
public void EnqueueTask(ITask task, Action<ICompletionState> callback)
{
_tasks.Enqueue(new CompletionState{ Task = task, Callback = callback });
}
void Work()
{
while (true)
{
CompletionState cs;
try
{
if (!_tasks.TryDequeue(out cs))
continue;
cs.Task.Execute();
cs.Callback(cs);
}
catch(Exception ex)
{
cs.Exception = ex;
cs.Callback(cs);
}
}
}
}

UnhandledException not called when exception thrown in another thread

According to the Microsoft documentation, when an unhandled exception occurs on a thread (from either the thread pool or created using the System.Threading.Thread class) the AppDomain.UnhandledException event should fire for the default AppDomain of the application. Here is the MSDN link which explains it after the second NOTE section.
But I cannot reproduce this behaviour, as far as I can tell from my test application it never fires the UnhandledException on either the default AppDomain or the AppDomain used to create the thread. Is the documentation wrong or my testing code?
using System;
using System.Runtime.ExceptionServices;
using System.Reflection;
public class Program
{
static void Main()
{
Program.HookAppDomainExceptions();
Test t = CreateTestInsideAppDomain("Nested1");
t.SetupNested1();
Console.ReadLine();
}
public static Test CreateTestInsideAppDomain(string appDomainName)
{
AppDomain nested1 = AppDomain.CreateDomain(appDomainName);
string executingName = Assembly.GetExecutingAssembly().FullName;
return (Test)nested1.CreateInstanceAndUnwrap(executingName, "Test");
}
public static void HookAppDomainExceptions()
{
AppDomain.CurrentDomain.FirstChanceException +=
new EventHandler<FirstChanceExceptionEventArgs>(FirstChanceException);
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
public static void FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{
Console.WriteLine("Domain:{0} FirstChanceException Handler",
AppDomain.CurrentDomain.FriendlyName);
}
public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("Domain:{0} UnhandledException Handler",
AppDomain.CurrentDomain.FriendlyName);
}
}
public class Test : MarshalByRefObject
{
private delegate void Nothing();
public void SetupNested1()
{
var start = new Nothing(Nested1ThreadStart);
start.BeginInvoke(null, null);
}
static void Nested1ThreadStart()
{
Program.HookAppDomainExceptions();
Test t = Program.CreateTestInsideAppDomain("Nested2");
t.SetupNested2();
}
public void SetupNested2()
{
Program.HookAppDomainExceptions();
Test t = Program.CreateTestInsideAppDomain("Nested3");
t.ThrowException();
}
public void ThrowException()
{
Program.HookAppDomainExceptions();
throw new ApplicationException("Raise Exception");
}
}
In your code UnhandledException isn't fired on any AppDomain, because if you call a delegate using BeginInvoke(), any exception that is thrown during its execution is handled and then rethrown when you call EndInvoke(), which you don't.
If you either call EndInvoke():
start.EndInvoke(start.BeginInvoke(null, null));
or execute the delegate synchronously:
start();
You get similar results: UnhandledException of the main domain is raised.
If instead, you do what the documentation says and start a new thread using the Thread class:
new Thread(Nested1ThreadStart).Start();
UnhandledException of Nested1 and the main app domain are raised.
So, to answer your question: The documentation is right. Your code is wrong. When you call delegate asynchronously using BeginInvoke(), you should always call EndInvoke() later.
I had this problem too. I used Observer Pattern to solve that.
you can implement an interface in your caller class that have a method which call from the other thread when an exception occurs.
Here's a link that shows how to implement this pattern Exploring the Observer Design Pattern

Async exception handling with void

I'm using Async CTP to write an IO heavy console app. But I'm having problems with exceptions.
public static void Main()
{
while (true) {
try{
myobj.DoSomething(null);
}
catch(Exception){}
Console.Write("done");
//...
}
}
//...
public async void DoSomething(string p)
{
if (p==null) throw new InvalidOperationException();
else await SomeAsyncMethod();
}
And the following happens: "done" gets written to the console, then I get the exception in the debugger, then I press continue my program exists.
What gives?
If you give your Console application an async-compatible context (e.g., AsyncContext (docs, source) from my AsyncEx library), then you can catch exceptions that propogate out of that context, even from async void methods:
public static void Main()
{
try
{
AsyncContext.Run(() => myobj.DoSomething(null));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
Console.Write("done");
}
public async void DoSomething(string p)
{
if (p==null) throw new InvalidOperationException();
else await SomeAsyncMethod();
}
When you call DoSomething() it basically creates a Task under the hood and starts that Task. Since you had a void signature, there is no Task object to signal back or that you could have blocked on, so execution fell straight through to done. Meanwhile the task throws an exception, which nobody is catching, which, I suspect, is why your program terminates.
I think the behavior you wanted is more like this:
public static void Main()
{
while (true) {
var t = myobj.DoSomething(null);
t.Wait();
if(t.HasException) {
break;
}
}
Console.Write("done");
//...
}
}
//...
public async Task DoSomething(string p)
{
if (p==null) throw new InvalidOperationException();
else await SomeAsyncMethod();
}
This will block on each DoSomething until it's done and exit the loop if DoSomething threw. Of course, then you are not really doing anything async. But from the pseudo code, i can't quite tell what you wanted to happen asynchronously.
Main take-away: Using void for an async method means that you loose the ability to get the exception unless you are awaiting that async method. As a sync call it basically just schedules work and the outcome disappears into the ether.

Categories

Resources