可能你已发现一个问题,我们无法使用VS对WPF项目创建单元测试(VS2005不行,VS2008我没试过,但据说也不行),这让人很郁闷,这里将介绍如何使用NUnit来对WPF项目创建单元测试并解决其中的难题(但利用NUnit来对WPF创建单元测试时并不会像针对.Net2.0一样容易,可能会出现一些小问题).
1,对普通类(非WPF UI组件)进行测试:
这和在.Net2.0中使用NUnit进行测试时一样,不会出现任何问题,参考下面的代码:
[TestFixture]
public class
ClassTest
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
[Test]
public void TestRun()
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
ClassLibrary1.Class1 obj = new ClassLibrary1.Class1();
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
double expected = 9;
double result = obj.GetSomeValue(3);
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
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
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
[Test]
public void TestRun()
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
WindowsApplication1.Window1 obj = new WindowsApplication1.Window1();
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
double expected = 9;
double result = obj.GetSomeValue(3);
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Assert.AreEqual(expected, result);
}
}
![wpfUnitTestError1.png](https://i-blog.csdnimg.cn/blog_migrate/1067f90c3e4fa578666d3afa8e2216af.png)
为了让调用线程为STA,我们可以编写一个辅助类CrossThreadTestRunner:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Threading;
using
System.Security.Permissions;
using
System.Reflection;
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace
TestUnit
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class CrossThreadTestRunner
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
private Exception lastException;
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void RunInMTA(ThreadStart userDelegate)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Run(userDelegate, ApartmentState.MTA);
}
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void RunInSTA(ThreadStart userDelegate)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Run(userDelegate, ApartmentState.STA);
}
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private void Run(ThreadStart userDelegate, ApartmentState apartmentState)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
lastException = null;
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Thread thread = new Thread(
delegate()
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
try
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
userDelegate.Invoke();
}
catch (Exception e)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
lastException = e;
}
});
thread.SetApartmentState(apartmentState);
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
thread.Start();
thread.Join();
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (ExceptionWasThrown())
ThrowExceptionPreservingStack(lastException);
}
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private bool ExceptionWasThrown()
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return lastException != null;
}
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
[ReflectionPermission(SecurityAction.Demand)]
private static void ThrowExceptionPreservingStack(Exception exception)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
FieldInfo remoteStackTraceString = typeof(Exception).GetField(
"_remoteStackTraceString",
BindingFlags.Instance | BindingFlags.NonPublic);
remoteStackTraceString.SetValue(exception, exception.StackTrace + Environment.NewLine);
throw exception;
}
}
}
并编写正确的测试代码:
[TestFixture]
public class
ClassTest
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
[Test]
public void TestRun()
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
CrossThreadTestRunner runner = new CrossThreadTestRunner();
runner.RunInSTA(
delegate
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine(Thread.CurrentThread.GetApartmentState());
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
WindowsApplication1.Window1 obj = new WindowsApplication1.Window1();
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
double expected = 9;
double result = obj.GetSomeValue(3);
Assert.AreEqual(expected, result);
});
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
}
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://dotnet.cnblogs.com/Images/OutliningIndicators/None.gif)
![wpfUnitTestOK.png](https://i-blog.csdnimg.cn/blog_migrate/f250f614309ded26dcbaca5d64cc7b90.png)
另外,使用NUnit时,您需要添加对nunit.framework.dll的引用,并对测试类添加[TestFixture]属性标记以及对测试方法添加[Test]属性标记,然后将生成的程序集用nunit.exe打开就可以了,关于NUnit的具体用法您可以参考其官方文档。