作者:
周銀輝 來源:
博客園 發布時間: 2009-02-27 15:08 閱讀: 4761 次 推薦: 0
原文鏈接 [收藏]
可能你已發現一個問題,我們無法使用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的具體用法您可以參考其官方文檔。
文章列表