可能你已发现一个问题,我们无法使用VS对WPF项目创建单元测试(VS2005不行,VS2008我没试过,但据说也不行),这让人很郁闷,这里将介绍如何使用NUnit来对WPF项目创建单元测试并解决其中的难题(但利用NUnit来对WPF创建单元测试时并不会像针对.Net2.0一样容易,可能会出现一些小问题).

1,对普通类(非WPF UI组件)进行测试:
这和在.Net2.0中使用NUnit进行测试时一样,不会出现任何问题,参考下面的代码:
    [TestFixture]
   
public class ClassTest
   
{
        [Test]
       
public void TestRun()
       
{
            ClassLibrary1.Class1 obj
= new ClassLibrary1.Class1();

           
double expected = 9;
           
double result = obj.GetSomeValue(3);

            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]
   
public class ClassTest
   
{
        [Test]
       
public void TestRun()
       
{
            WindowsApplication1.Window1 obj
= new WindowsApplication1.Window1();

           
double expected = 9;
           
double result = obj.GetSomeValue(3);

            Assert.AreEqual(expected, result);
        }

    }





为了让调用线程为STA,我们可以编写一个辅助类CrossThreadTestRunner:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Security.Permissions;
using System.Reflection;

namespace TestUnit
{
   
public class CrossThreadTestRunner
   
{
       
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(
             
delegate()
             
{
                 
try
                 
{
                      userDelegate.Invoke();
                  }

                 
catch (Exception e)
                 
{
                      lastException
= e;
                  }

              }
);
            thread.SetApartmentState(apartmentState);

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

           
if (ExceptionWasThrown())
                ThrowExceptionPreservingStack(lastException);
        }


       
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;
        }

    }

}


并编写正确的测试代码:
    [TestFixture]
   
public class ClassTest
   
{
        [Test]
       
public void TestRun()
       
{
           
            CrossThreadTestRunner runner
= new CrossThreadTestRunner();
            runner.RunInSTA(
             
delegate
             
{
                  Console.WriteLine(Thread.CurrentThread.GetApartmentState());

                  WindowsApplication1.Window1 obj
= new WindowsApplication1.Window1();

                 
double expected = 9;
                 
double result = obj.GetSomeValue(3);
                  Assert.AreEqual(expected, result);
              }
); 

        }

  }






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

DEMO          NUnit