Apr
10
Written by:
brettright
Saturday, April 10, 2010 9:58 PM
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;
}
}
1 comment(s) so far...
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
|