I will outline how to setup NUnit testing with WinFX 1.0 (Feb CTP) in order to UnitTest an Avalon Window.
Points of technology that drive the details.
Create a new class in your test Assembly to hold the NUnitHelper namespace and class.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Permissions;
using System.Threading;
using System.Text;
namespace NUnitHelpers
{
class CrossThreadTestRunner
{
private ThreadStart userDelegate;
private Exception lastException;</p>
public void RunSTA(ThreadStart userdelegate)
{
this.userDelegate = userdelegate;
Thread t = new Thread(new ThreadStart(this.MultiThreadedWorker));
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
if (lastException != null)
ThrowExceptionPreservingStack(lastException);
}
public void Run(ThreadStart userdelegate)
{
this.userDelegate = userdelegate;
Thread t = new Thread(new ThreadStart(this.MultiThreadedWorker));
t.Start();
t.Join();
if (lastException != null)
ThrowExceptionPreservingStack(lastException);
}
[ReflectionPermission(SecurityAction.Demand)]
private void ThrowExceptionPreservingStack(Exception e)
{
FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic);
remoteStackTraceString.SetValue(e, e.StackTrace + Environment.NewLine);
throw e;
}
private void MultiThreadedWorker()
{
try
{
userDelegate.Invoke();
}
catch (Exception e)
{
lastException = e;
}
}
}
}
This is adapted from Peter Provost’s original code. First, I compressed his constructor/method calls into just the method call. This allows me the cleanest calling-code that I can get with minimal effort. Second, I duplicated Run() into RunSTA() in order to test STA threads that are executing the WPF Window.
Now my calling class (the actual NUnit testing class) can use this new helper to correctly process WPF testing (handling Assert exceptions instead of raising UnhandledException error inside NUnit’s execution path.) :
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Interop;
using NUnit.Framework;
namespace NunitTest
{
[TestFixture]
public class Class1
{
NUnitHelpers.CrossThreadTestRunner runner = new NUnitHelpers.CrossThreadTestRunner();
[Test]
public void truthtest()
{
Assert.IsTrue(true);
}
[Test]
public void falsetest()
{
Assert.IsTrue(false);
}
public static void TestAddressbookWorker()
{
Window ab = new AddressBookLib.Window1();
ab.Show();
ab.Close();
ab = null;
Assert.IsTrue(false, "Forced NUNit Failure");
}
[Test]
public void TestAddressBook()
{
runner.RunSTA(new ThreadStart(TestAddressbookWorker));
}
}
}
Here each test method requires a worker that contains the actual testing code. The calling test is simply delegating the worker method into the CrossThreadTestRunner instance. Using RunSTA() starts an STA thread and then the WPF window is started happily.
------ Test started: Assembly: NunitTest.dll ------
TestCase 'NunitTest.Class1.falsetest' failed:
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\Class1.cs(26,0): at NunitTest.Class1.falsetest()
TestCase 'NunitTest.Class1.TestAddressBook' failed: Forced NUNit Failure
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\Class1.cs(35,0): at NunitTest.Class1.TestAddressbookWorker()
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\CrossThreadTestRunner.cs(55,0): at NUnitHelpers.CrossThreadTestRunner.MultiThreadedWorker()
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\CrossThreadTestRunner.cs(48,0): at NUnitHelpers.CrossThreadTestRunner.ThrowExceptionPreservingStack(Exception e)
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\CrossThreadTestRunner.cs(24,0): at NUnitHelpers.CrossThreadTestRunner.RunSTA(ThreadStart userdelegate)
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\Class1.cs(42,0): at NunitTest.Class1.TestAddressBook()
1 passed, 2 failed, 0 skipped, took 21.25 seconds.
|
You Passed 8th Grade Math |
![]() Congratulations, you got 10/10 correct! |
Dan and Kim Steinberg have had a terrible loss of their daugter Elena.
Dan’s thoughts are here.
Here’s how to use AJAX from the onchange event of the SELECT element :
I used an extra ERB step in my view in order to reduce chaos being created by double quotes. This example actually triggers two AJAX calls on the event to update different targets (two cascaded SELECTs).
<% onchange_ajax = remote_function(:update => :model_select,
:url => { :action => :ajax_list_model_select },
:with=> " 'project_id='+value") + ";" +
remote_function(:update => :category_select,
:url => { :action => :ajax_list_category_select },
:with=> " 'project_id='+value")
%>
<%= select("project", "id", @projects,
{ :include_blank=>true },
{
nchange => onchange_ajax } ) %>
The result has the SELECT element with onchange=”new Ajax.Updater…”