Welcome to Habanero Land!   Register  |  Login
You are here:   Blogs
  So, why do you need to register to download Habanero and access the additional materials? 
The success of any open source project is based on the community built around it. So go ahead, register / login and get involved!
Blog Roll
 
Categories
 
Search Blog
 
 
Habanero Cloud
 
Habanero Archive
 
Recent Blog Posts
 
 
Apr 10

Written by: brettright
Saturday, April 10, 2010 9:58 PM  RssIcon

I am not going to delve too deeply into the arguments on whether the User Interface should be tested or not. The principles of Unit Testing of User Interfaces are hotly debated in various Blogs. In this particular project I was using a third party control that was returning me a result of and I really needed to test my interaction with this control. The problem was the Control had to be run in STA and most Test Runners run in MTA. This seemed fairly simple since there is a [RequireSTA] in NUnit. Unforturtunately this did worked with some Test Runners but others seemed to ignore this attribute. The solution therefore became to create a Cross Thread Runner.

Below is the code. It is fairly well documented with an example and is included in Habanero.Base.  

This is a modification of the code found here 

"http://stackoverflow.com/questions/343521
/using-nunit-2-5-requiresstaattribute-with-teamcity-4"

///


/// This class is used when you specifically require some code method to run in MTA or STA thread.
///

///
/// Unfortunately not all test runners implement the [RequireSTA] or other attributes such as config files.
/// This may result in tests passing in NUnit GUI failing on the build server or failing on another developer's machine
/// e.g. someone using Resharper. By using this Component you can ensure that the Individual test runs as required.
/// The common requirement for STA threading is a visual component under test.
///

///
///
///[Test]

///public void Test_SetSolution_WithNull_ShouldRaiseError()

///{

///    CrossThreadRunner runner = new CrossThreadRunner();

///     runner.RunInSTA(delegate

///      {

/// //---------------Set up test pack-----------------

/// ReportSourceDefCreatorControl creatorControl = new ReportSourceDefCreatorControl();

/// //---------------Assert Precondition-------------

/// //---------------Execute Test---------------------

/// try

///   {

///      creatorControl.SetSolution(null);

///      Assert.Fail("expected ArgumentNullException");

///   }

/// //---------------Test Result--------------------

///   catch (ArgumentNullException ex)

///   {

///    StringAssert.Contains("Value cannot be null", ex.Message);

///    StringAssert.Contains("solution", ex.ParamName);

///   }

///});

///}

///

/// 
public class CrossThreadRunner
    {
        private Exception _lastException;

        ///
        /// Run the specified delegate in the MTA Thread.
        ///

        ///The delegate to be run in the MTA Thread
        public void RunInMTA(ThreadStart userDelegate)
        {
            Run(userDelegate, ApartmentState.MTA);
        }

        ///
        /// Run the specified funcion delegate in the MTA Thread and return the return value of the function.
        ///

        /// The function delegate to be run in the MTA Thread
        /// The return value of the function
        public TReturn RunInMTA(Function function)
        {
            TReturn returnValue = default(TReturn);
            RunInMTA(delegate
            {
                returnValue = function();
            });
            return returnValue;
        }

        ///
        /// Run the specified delegate in the STA Thread.
        ///

        ///The delegate to be run in the STA Thread

        public void RunInSTA(ThreadStart userDelegate)
        {
            Run(userDelegate, ApartmentState.STA);
        }

        ///
        /// Run the specified funcion delegate in the STA Thread and return the return value of the function.
        ///

        /// The function delegate to be run in the STA Thread

        /// The return value of the function
        public TReturn RunInSTA(Function function)
        {
            TReturn returnValue = default(TReturn);
            RunInSTA(delegate
            {
                returnValue = function();
            });
            return returnValue;
        }

        private void Run(ThreadStart userDelegate, ApartmentState apartmentState)
        {
            _lastException = null;

            Thread thread = new Thread(() => MultiThreadedWorker(userDelegate));
            thread.SetApartmentState(apartmentState);

            thread.Start();
            thread.Join();

            if (ExceptionWasThrown())
                ThrowExceptionPreservingStack(_lastException);
        }

        private void MultiThreadedWorker(ThreadStart userDelegate)
        {
            try
            {
                userDelegate.Invoke();
            }
            catch (Exception e)
            {
                _lastException = e;
            }
        }

        private bool ExceptionWasThrown()
        {
            return _lastException != null;
        }

        [ReflectionPermission(SecurityAction.Demand)]
        private static void ThrowExceptionPreservingStack(Exception exception)
        {
            FieldInfo remoteStackTraceString = typeof(Exception).GetField(
                "_remoteStackTraceString",
                BindingFlags.Instance | BindingFlags.NonPublic);
            remoteStackTraceString.SetValue(exception, exception.StackTrace + Environment.NewLine);
            throw exception;
        }
 }

Tags:
Categories:

1 comment(s) so far...


Gravatar

Re: Cross Thread Runner - Testing User Controls in an STA Thread with NUnit

///
/// This class is used when you specifically require a single test method to run in MTA or STA thread.
/// Unfortunately not all test runners implement the [RequireSTA] or other attributes such as config files.
/// This may result in tests passing in NUnit GUI failing on the build server or failing on another developer's machine
/// e.g. someone using Resharper. By using this Component you can ensure that the Individual test runs as required.
/// The common requirement for STA threading is a visual component under test.
///
/// [Test]
/// public void Test_SetSolution_WithNull_ShouldRiaseError()
/// {
/// CrossThreadTestRunner runner = new CrossThreadTestRunner();
/// runner.RunInSTA(
/// delegate
/// {
/// //---------------Set up test pack-------------------
/// ReportSourceDefCreatorControl creatorControl = new ReportSourceDefCreatorControl();
/// //---------------Assert Precondition----------------
/// //---------------Execute Test ----------------------
/// try
/// {
/// creatorControl.SetSolution(null);
/// Assert.Fail("expected ArgumentNullException");
/// }
/// //---------------Test Result -----------------------
/// catch (ArgumentNullException ex)
/// {
/// StringAssert.Contains("Value cannot be null", ex.Message);
/// StringAssert.Contains("solution", ex.ParamName);
/// }
/// });
/// }
///
///
public class CrossThreadRunner
{
private Exception _lastException;

public void RunInMTA(ThreadStart userDelegate)
{
Run(userDelegate, ApartmentState.MTA);
}

public void RunInSTA(ThreadStart userDelegate)
{
Run(userDelegate, ApartmentState.STA);
}

private void Run(ThreadStart userDelegate, ApartmentState apartmentState)
{
_lastException = null;

Thread thread = new Thread(() => MultiThreadedWorker(userDelegate));
thread.SetApartmentState(apartmentState);

thread.Start();
thread.Join();

if (ExceptionWasThrown())
ThrowExceptionPreservingStack(_lastException);
}

private void MultiThreadedWorker(ThreadStart userDelegate)
{
try
{
userDelegate.Invoke();
}
catch (Exception e)
{
_lastException = e;
}
}

private bool ExceptionWasThrown()
{
return _lastException != null;
}

[ReflectionPermission(SecurityAction.Demand)]
private static void ThrowExceptionPreservingStack(Exception exception)
{
FieldInfo remoteStackTraceString = typeof(Exception).GetField(
"_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic);
remoteStackTraceString.SetValue(exception, exception.StackTrace + Environment.NewLine);
throw exception;
}
}

By GloryDev on   Friday, September 03, 2010 9:49 AM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Add Comment   Cancel 

This website is best viewed in Internet Explorer 7 & 8; Firefox; Opera; & Safari.

Get Habanero at SourceForge.net. Fast, secure and Free Open Source software downloads