只是爲了保持完整性,我經常要運行測試,控制檯應用程序,僅僅是因爲我覺得它更容易調試這出於某種原因...多年來,我創建了幾個小測試助手來幫助我;我想你可以很容易地將它們用於CI解決方案。
我明白這完全不是你的問題;然而,因爲你正在尋找一個CI解決方案並提到視覺工作室,這應該很好地解決它。
爲了讓你知道,我的小框架比這個稍大一些,但缺少的東西很容易添加。基本上,我遺漏的是日誌記錄的一切,以及我測試不同應用程序域中的不同程序集的事實(因爲可能的DLL衝突和狀態)。更多關於下面的內容。
有一點需要注意的是,我沒有在下面的過程中發現任何異常。我的主要重點是在故障排除時輕鬆調試您的應用程序。我有一個獨立的(但類似的)CI實現,基本上在下面的註釋點上增加了try/catches。
這個方法只有一個方法:Visual Studio不會複製你引用的所有程序集;它只會複製你在代碼中使用的程序集。一個簡單的解決方法是引入一個方法(從不調用),它在您正在測試的DLL中使用一種類型。這樣,你的程序集將被複制,並且一切都將很好地工作。
下面的代碼:
static class TestHelpers
{
public static void TestAll(this object o)
{
foreach (MethodInfo meth in o.GetType().GetMethods().
Where((a) => a.GetCustomAttributes(true).
Any((b) => b.GetType().Name.Contains("TestMethod"))))
{
Console.WriteLine();
Console.WriteLine("--- Testing {0} ---", meth.Name);
Console.WriteLine();
// Add exception handling here for your CI solution.
var del = (Action)meth.CreateDelegate(typeof(Action), o);
del();
// NOTE: Don't use meth.Invoke(o, new object[0]); ! It'll eat your exception!
Console.WriteLine();
}
}
public static void TestAll(this Assembly ass)
{
HashSet<AssemblyName> visited = new HashSet<AssemblyName>();
Stack<Assembly> todo = new Stack<Assembly>();
todo.Push(ass);
HandleStack(visited, todo);
}
private static void HandleStack(HashSet<AssemblyName> visited, Stack<Assembly> todo)
{
while (todo.Count > 0)
{
var assembly = todo.Pop();
// Collect all assemblies that are related
foreach (var refass in assembly.GetReferencedAssemblies())
{
TryAdd(refass, visited, todo);
}
foreach (var type in assembly.GetTypes().
Where((a) => a.GetCustomAttributes(true).
Any((b) => b.GetType().Name.Contains("TestClass"))))
{
// Add exception handling here for your CI solution.
var obj = Activator.CreateInstance(type);
obj.TestAll();
}
}
}
public static void TestAll()
{
HashSet<AssemblyName> visited = new HashSet<AssemblyName>();
Stack<Assembly> todo = new Stack<Assembly>();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
TryAdd(assembly.GetName(), visited, todo);
}
HandleStack(visited, todo);
}
private static void TryAdd(AssemblyName ass, HashSet<AssemblyName> visited, Stack<Assembly> todo)
{
try
{
var reference = Assembly.Load(ass);
if (reference != null &&
!reference.GlobalAssemblyCache && // Ignore GAC
reference.FullName != null &&
!reference.FullName.StartsWith("ms") && // mscorlib and other microsoft stuff
!reference.FullName.StartsWith("vshost") && // visual studio host process
!reference.FullName.StartsWith("System")) // System libraries
{
if (visited.Add(reference.GetName())) // We don't want to test assemblies twice
{
todo.Push(reference); // Queue assembly for processing
}
}
}
catch
{
// Perhaps log something here... I currently don't because I don't care...
}
}
}
如何使用此代碼:
- 你可以簡單地調用
TestHelpers.TestAll()
測試所有組件,引用的程序集,間接引用的組件等,這可能是什麼你想在CI中做。
- 您可以致電
TestHelpers.TestAll(assembly)
測試帶有所有引用程序集的單個裝配。當你在多個程序集中拆分測試和/或在調試時,這會很有用。
- 您可以致電
new MyObject().TestAll()
調用單個對象中的所有測試。這在調試時特別有用。
如果您使用的是像我這樣的應用程序域,您應該爲從一個文件夾動態加載的DLL構建一個應用程序域,然後使用TestAll。另外,如果您使用臨時文件夾,則可能需要在測試之間清空該文件夾。這樣,多個測試框架版本和多個測試將不會相互影響。特別是如果你的測試使用狀態(例如靜態變量),這可能是一個好習慣。有很多CreateInstanceAndUnwrap
在線的例子可以幫助你解決這個問題。
有一點需要注意的是,我使用代表而不是method.Invoke
。這基本上意味着你的異常對象不會被Reflection所吃掉,這意味着你的調試器不會被破壞。另外請注意,我通過名稱檢查屬性,這意味着這將與不同的框架一起工作 - 只要屬性名稱匹配。
HTH
是的。但我需要1份報告。 – Nahum 2014-12-16 15:59:58
對於那些給-1,你能評論的原因,所以我有機會改善答案? – jessehouwing 2014-12-17 12:50:35
我擔心他們只是爲了賞金而戰。我之前看到過這種行爲。 – Nahum 2014-12-17 14:51:20