Solo per completezza, spesso mi vogliono eseguire i test come applicazione console, semplicemente perché lo trovo molto più facile eseguire il debug di questo, per qualche motivo ... Nel corso degli anni ho creato alcuni piccoli aiutanti di test per aiutarmi; Suppongo che tu possa usarli con la tua soluzione CI abbastanza facilmente.
Capisco che questa non è la tua domanda interamente; tuttavia, dal momento che stai cercando una soluzione CI e menzioni lo studio visivo, questo dovrebbe risolverlo abbastanza bene.
Solo per farvi sapere, il mio piccolo quadro è un po 'più grande di questo, ma le cose che mancano sono abbastanza facili da aggiungere. Fondamentalmente ciò che ho tralasciato è tutto per la registrazione e il fatto che eseguo test su diversi assembly in domini di app diversi (a causa di possibili conflitti e stati delle DLL). Maggiori informazioni su quello sotto.
Una cosa da notare è che non rilevo eccezioni nel processo sottostante. Il mio obiettivo principale è semplificare il debug dell'applicazione durante la risoluzione dei problemi. Ho un'implementazione separata (ma simile) per l'IC che fondamentalmente aggiunge try/catch ai commenti qui sotto.
C'è solo un fermo per questo metodo: Visual Studio non copierà tutti gli assiemi a cui si fa riferimento; copierà solo gli assembly che utilizzi nel codice. Una soluzione semplice per questo è di introdurre un metodo (che non viene mai chiamato) che utilizza un tipo nella DLL che stai testando. In questo modo, il tuo assembly verrà copiato e tutto funzionerà correttamente.
Ecco il codice:
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...
}
}
}
Come per utilizzare questo codice:
- Si può semplicemente chiamare
TestHelpers.TestAll()
per testare tutte le assemblee, assemblee a cui fa riferimento, assemblee indirettamente fa riferimento, ecc Questo è probabilmente ciò che vuoi fare in CI.
- È possibile chiamare
TestHelpers.TestAll(assembly)
per testare un singolo assieme con tutti i gruppi di riferimento. Questo può essere utile quando dividi i test su più assiemi e/o quando esegui il debug.
- È possibile chiamare
new MyObject().TestAll()
per richiamare tutti i test in un singolo oggetto. Questo è particolarmente utile durante il debug.
Se stai usando AppDomain come me, si dovrebbe fare un singolo dominio di applicazione per una DLL di caricare in modo dinamico da una cartella e utilizzare TestAll su questo. Inoltre, se si utilizza una cartella di lavoro, è consigliabile svuotare quella tra i test. In questo modo, più versioni di test framework e test multipli non interagiranno tra loro.In particolare se i test utilizzano lo stato (ad esempio variabili statiche), questa potrebbe essere una buona pratica. Ci sono un sacco di esempi per CreateInstanceAndUnwrap
online che ti aiuteranno in questo.
Una cosa da notare è che io uso un delegato invece del method.Invoke
. Questo significa fondamentalmente che il tuo oggetto di eccezione non verrà mangiato da Reflection, il che significa che il tuo debugger non sarà interrotto. Nota anche che controllo gli attributi per nome, il che significa che funzionerà con diversi framework, purché i nomi degli attributi corrispondano.
HTH
Sì. Ma ho bisogno di 1 rapporto. – Nahum
Per quelli che danno i voti di -1, puoi commentare il motivo per cui ho la possibilità di migliorare la risposta? – jessehouwing
Ho paura che combattano solo per la taglia. Ho visto questo comportamento prima. – Nahum