可能你已发现一个问题,我们无法使用VS对WPF项目创建单元测试(VS2005不行,VS2008我没试过,但据说也不行),这让人很郁闷,这里将介绍如何使用NUnit来对WPF项目创建单元测试并解决其中的难题(但利用NUnit来对WPF创建单元测试时并不会像针对.Net2.0一样容易,可能会出现一些小问题).
1,对普通类(非WPF UI组件)进行测试:
 这和在.Net2.0中使用NUnit进行测试时一样,不会出现任何问题,参考下面的代码:
 [TestFixture]
    [TestFixture] public class ClassTest
    public class ClassTest {
    { [Test]
        [Test] public void TestRun()
        public void TestRun() {
        { ClassLibrary1.Class1 obj = new ClassLibrary1.Class1();
            ClassLibrary1.Class1 obj = new ClassLibrary1.Class1();
 double expected = 9;
            double expected = 9; double result = obj.GetSomeValue(3);
            double result = obj.GetSomeValue(3);
 Assert.AreEqual(expected, result);
            Assert.AreEqual(expected, result); }
        } }
     }
2,对WPF UI组件进行测试
 使用NUnit对WPF UI组件(比如MyWindow,MyUserControl)进行测试的时候,NUnit会报如下异常:“The calling thread must be STA, because many UI components require this”。
下面是错误的测试代码:
 [TestFixture]
[TestFixture] public class ClassTest
    public class ClassTest {
    { [Test]
        [Test] public void TestRun()
        public void TestRun() {
        { WindowsApplication1.Window1 obj = new WindowsApplication1.Window1();
            WindowsApplication1.Window1 obj = new WindowsApplication1.Window1();
 double expected = 9;
            double expected = 9; double result = obj.GetSomeValue(3);
            double result = obj.GetSomeValue(3);
 Assert.AreEqual(expected, result);
            Assert.AreEqual(expected, result); }
        } }
     }

为了让调用线程为STA,我们可以编写一个辅助类CrossThreadTestRunner:
 using System;
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Text;
using System.Text; using System.Threading;
using System.Threading; using System.Security.Permissions;
using System.Security.Permissions; using System.Reflection;
using System.Reflection;
 namespace TestUnit
namespace TestUnit {
{ public class CrossThreadTestRunner
    public class CrossThreadTestRunner {
    { private Exception lastException;
        private Exception lastException;
 public void RunInMTA(ThreadStart userDelegate)
        public void RunInMTA(ThreadStart userDelegate) {
        { Run(userDelegate, ApartmentState.MTA);
            Run(userDelegate, ApartmentState.MTA); }
        }
 public void RunInSTA(ThreadStart userDelegate)
        public void RunInSTA(ThreadStart userDelegate) {
        { Run(userDelegate, ApartmentState.STA);
            Run(userDelegate, ApartmentState.STA); }
        }
 private void Run(ThreadStart userDelegate, ApartmentState apartmentState)
        private void Run(ThreadStart userDelegate, ApartmentState apartmentState) {
        { lastException = null;
            lastException = null;
 Thread thread = new Thread(
            Thread thread = new Thread( delegate()
              delegate() {
              { try
                  try {
                  { userDelegate.Invoke();
                      userDelegate.Invoke(); }
                  } catch (Exception e)
                  catch (Exception e) {
                  { lastException = e;
                      lastException = e; }
                  } });
              }); thread.SetApartmentState(apartmentState);
            thread.SetApartmentState(apartmentState);
 thread.Start();
            thread.Start(); thread.Join();
            thread.Join();
 if (ExceptionWasThrown())
            if (ExceptionWasThrown()) ThrowExceptionPreservingStack(lastException);
                ThrowExceptionPreservingStack(lastException); }
        }
 private bool ExceptionWasThrown()
        private bool ExceptionWasThrown() {
        { return lastException != null;
            return lastException != null; }
        }
 [ReflectionPermission(SecurityAction.Demand)]
        [ReflectionPermission(SecurityAction.Demand)] private static void ThrowExceptionPreservingStack(Exception exception)
        private static void ThrowExceptionPreservingStack(Exception exception) {
        { FieldInfo remoteStackTraceString = typeof(Exception).GetField(
            FieldInfo remoteStackTraceString = typeof(Exception).GetField( "_remoteStackTraceString",
              "_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
              BindingFlags.Instance | BindingFlags.NonPublic); remoteStackTraceString.SetValue(exception, exception.StackTrace + Environment.NewLine);
            remoteStackTraceString.SetValue(exception, exception.StackTrace + Environment.NewLine); throw exception;
            throw exception; }
        } }
    } }
}
并编写正确的测试代码:
 [TestFixture]
    [TestFixture] public class ClassTest
    public class ClassTest {
    { [Test]
        [Test] public void TestRun()
        public void TestRun() {
        { 
             CrossThreadTestRunner runner = new CrossThreadTestRunner();
            CrossThreadTestRunner runner = new CrossThreadTestRunner(); runner.RunInSTA(
            runner.RunInSTA( delegate
              delegate {
              { Console.WriteLine(Thread.CurrentThread.GetApartmentState());
                  Console.WriteLine(Thread.CurrentThread.GetApartmentState());
 WindowsApplication1.Window1 obj = new WindowsApplication1.Window1();
                  WindowsApplication1.Window1 obj = new WindowsApplication1.Window1();
 double expected = 9;
                  double expected = 9; double result = obj.GetSomeValue(3);
                  double result = obj.GetSomeValue(3); Assert.AreEqual(expected, result);
                  Assert.AreEqual(expected, result); });
              });   
 }
        } }
  }



另外,使用NUnit时,您需要添加对nunit.framework.dll的引用,并对测试类添加[TestFixture]属性标记以及对测试方法添加[Test]属性标记,然后将生成的程序集用nunit.exe打开就可以了,关于NUnit的具体用法您可以参考其官方文档。
 
    
