0

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();
}

the exception I'm trying to test 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?

Anton Serov
  • 174
  • 2
  • 12
  • You have to add try/catch to each of those thread methods to capture any exceptions inside. Note that if you were to switch to long running tasks you would have support for this out of the box. – Lasse V. Karlsen Jul 19 '21 at 09:26
  • @LasseV.Karlsen imagine if I started working on some legacy project and didn't know where the issue is, also assuming I used test-driven development methodology. Before changing anything I first wanted to add unit tests to be sure they failed. Then I wanted to fix the issue and made sure the tests pass with the fix. – Anton Serov Jul 19 '21 at 09:43
  • 1
    Basically no, there isn't a good way to do what you are asking. The reason why it's being caught in the debugger, is because you are actively stepping into the thread, that is causing the exception, but the "main" thread, is not aware your debugger is doing that, just as it isn't aware that the spawned thread caused an exception. (You have written the main code, to ignore the thread) Like people are saying here, switch to long-running tasks instead. There is a feature-set built into to C# that allows you to error handle from failed sub-threads. Duplicate of: – Morten Bork Jul 19 '21 at 10:15
  • https://stackoverflow.com/questions/1554181/exception-handling-in-threads – Morten Bork Jul 19 '21 at 10:21

1 Answers1

2

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.

JonasH
  • 28,608
  • 2
  • 10
  • 23