Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to abort unit test from another thread?

I have unit test sample code that basically runs 2 threads: the main test thread and another thread I'm launching that is supposed to fail the test execution after some time (this is basically a timeout thread)

Code is as follows:

[TestClass]
public class SomeTestClass
{
    [TestInitialize]
    public void BeforeTest()
    {
        var task = new Task(abortIfTestStilRunsAfterTimeout);
        task.Start();
    }

    [TestMethod]
    public void TestMethod()
    {
        Thread.Sleep(5000);
    }

    private void abortIfTestStilRunsAfterTimeout()
    {
        Assert.Fail("timeout passed!");

    }
}

Well, I was expecting that the TestMethod() test should fail but what actually happens is that the task thread that runs the Assert.Fail method gets an exception while the other thread keeps on running and the test passes.

I'm looking for a way to fail the test method

like image 265
Tzahi Avatar asked Oct 08 '22 03:10

Tzahi


2 Answers

You can try and get a reference to the test thread and call Abort() on it. You can pass an exception state object to Abort() which you can use to pass a fail message:

[TestClass]
public class SomeTestClass
{
    Thread testThread;

    [TestInitialize]
    public void BeforeTest()
    {
        testThread = Thread.CurrentThread;
        var task = new Task(abortIfTestStilRunsAfterTimeout);
        task.Start();
    }

    [TestMethod]
    public void TestMethod()
    {
        try
        {
            Thread.Sleep(5000);
        }
        catch (ThreadAbortException e)
        {
             Assert.Fail((string)e.ExceptionState);
        }
    }

    private void abortIfTestStilRunsAfterTimeout()
    {
        testThread.Abort("timeout passed!");
    }
}

In case you do not want to modify some 100 tests you can use a tool like PostSharp to modify your test cases by inserting the try {} catch {} logic around each test case for you. All it boils down to is to write an attribute and decorate you test assembly with it (it's called Aspect Oriented Programming and the framework in PostSharp for that is called Laos).

like image 72
ChrisWue Avatar answered Oct 12 '22 13:10

ChrisWue


The idea is simple - just run main test logic in a task/thread, and just put timeout handling code in the test method/test initialize. Some kind of Inversion of Control:

// Test class level
ManualResetEvent mre = new ManualResetEvent(false);                                

[TestMethod]     
public void TestMethod()     
{                     
    // start waiting task
    Task task = Task.Factory.StartNew(() =>
        {
            // Test Body HERE!
            // ... 

            // if test passed - set event explicitly
            mre.Set();
        });


    // Timeout handling logic, 
    // !!! I believe you can put it once in the TestInitialize, just check
    // whether Assert.Fail() fails the test when called from TestInitialize
    mre.WaitOne(5000);

    // Check whether ManualResetEvent was set explicitly or was timeouted
    if (!mre.WaitOne(0))
    {
        task.Dispose();
        Assert.Fail("Timeout");
    }                
}                    

PS: Regarding WaitOne(0) trick, MSDN:

If millisecondsTimeout is zero, the method does not block. It tests the state of the wait handle and returns immediately.

like image 38
sll Avatar answered Oct 12 '22 13:10

sll