unitarias - unit test c#
Como ejecutar todas las pruebas en soluciĆ³n. (5)
Lea la parte de Precaución en su propio enlace. Ya no se admite en VST 2012 de la forma en que lo haces.
Quizás esta versión de actualización pueda ayudar: http://msdn.microsoft.com/en-us/library/ms182490.aspx
Parece que puedo ejecutar todas mis pruebas en la solución de una sola vez desde la línea de comandos usando MSTest si uso el indicador / testmetadata como se describe aquí: http://msdn.microsoft.com/en-us/library/ms182487.aspx
Estoy ejecutando las pruebas de unidad de base de datos de SQL Server en Visual Studio 2013, donde parece que no tengo ningún archivo vsmdi, y tampoco puedo encontrar una manera de agregar uno. Intenté crear un archivo de configuración de prueba, pero no descubre ninguna prueba (muestra "No hay pruebas para ejecutar") cuando invoco MSTest.
¿Hay alguna manera de hacer que MSTest ejecute todas mis pruebas en una solución VS2013?
MsTest es una especie de marco de prueba "obsoleto" de Visual Studio 2013. Todavía se usa para algunos tipos de pruebas, pero muchas de las otras pruebas que se pueden ejecutar ahora se encuentran en el nuevo Agile Test Runner. Ahora, las pruebas unitarias de la base de datos SQL parecen estar todavía en la categoría de "Algunos tipos" y deben ejecutarse a través de MsTest.exe.
La forma más fácil es usar el /TestContainer
línea de comandos /TestContainer
y usar un patrón de nomenclatura para sus proyectos de prueba. De esa forma, puede tomar rápidamente todos los ensamblajes con un patrón de denominación específico y luego enviarlos a MsTest. El comando powershell simple podría usarse para agarrar todos los archivos que se adhieren a su patrón y luego enviarlos a la línea de comandos.
El vsmdi todavía funcionará en Visual Studio 2013, pero el editor se ha eliminado de la herramienta, y además ya no hay ninguna plantilla para ello. Así que es muy difícil de usar. Esto es lo que Microsoft tiene que decir sobre los VSDMI:
Las listas de pruebas de precaución ya no son totalmente compatibles con Visual Studio 2012:
- No puedes crear nuevas listas de prueba.
- No puede ejecutar pruebas de la lista de pruebas desde Visual Studio.
- Si actualizó desde Visual Studio 2010 y tenía una lista de prueba en su solución, puede continuar editándola en Visual Studio.
- Puede continuar ejecutando la lista de pruebas usando mstest.exe desde la línea de comandos, como se describe anteriormente.
- Si estaba usando una lista de prueba en su definición de compilación, puede continuar usándola.
Básicamente te están diciendo que dejes de usar esta técnica y que uses una combinación de TestCategory
para crear grupos de pruebas fáciles de ejecutar.
Como puede agregar múltiples parámetros para los contenedores de prueba, todos pueden agruparlos en una llamada:
/testcontainer:[file name] Load a file that contains tests. You can
Specify this option more than once to
load multiple test files.
Examples:
/testcontainer:mytestproject.dll
/testcontainer:loadtest1.loadtest
MsTest /testcontainer:assemblyone.dll /testcontainer:assemblytwo.dll /testcontainer:assembly3.dll
Para ejecutar MsTest en varios ensamblajes a la vez. Y no (todavía) haga uso de XUnit .NET o NUnit, ya que no se pueden combinar en un solo informe sin cambiar al nuevo corredor de pruebas Agile.
No sé si esto te ayudará o no, pero uso Invoke-MsBuild . Es un módulo de PowerShell que debería hacer exactamente lo que necesitas. No sé si estaba buscando una solución PowerShell o no, ¡pero funciona muy bien!
También tiene un script hermano, Invoke-MsTest para ejecutar MsTest en lugar de MsBuild.
Quería cerrar esta pregunta abierta. Mi intención era ejecutar todas las pruebas de una sola vez desde el servidor de CI de Hudson, así que escribí una aplicación de consola básica para encontrar e invocar MSTest en todos los archivos DLL dentro de la carpeta de soluciones. Esta aplicación se ejecuta después de que el proyecto se construye en modo de lanzamiento.
string execId = null;
string className = null;
string testName = null;
string testResult = null;
string resultLine = null;
List<string> results = new List<string>();
XmlDocument resultsDoc = new XmlDocument();
XmlNode executionNode = null;
XmlNode testMethodNode = null;
// Define the test instance settings
Process testInstance = null;
ProcessStartInfo testInfo = new ProcessStartInfo()
{
UseShellExecute = false,
CreateNoWindow = true,
};
// Fetch project list from the disk
List<string> excluded = ConfigurationManager.AppSettings["ExcludedProjects"].Split('','').ToList();
DirectoryInfo assemblyPath = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
DirectoryInfo[] directories = assemblyPath.Parent.Parent.Parent.Parent.GetDirectories();
// Create a test worklist
List<string> worklist = directories.Where(t => !excluded.Contains(t.Name))
.Select(t => String.Format(ConfigurationManager.AppSettings["MSTestCommand"], t.FullName, t.Name))
.ToList();
// Start test execution
Console.WriteLine("Starting Execution...");
Console.WriteLine();
Console.WriteLine("Results Top Level Tests");
Console.WriteLine("------- ---------------");
// Remove any existing run results
if (File.Exists("UnitTests.trx"))
{
File.Delete("UnitTests.trx");
}
// Run each project in the worklist
foreach (string item in worklist)
{
testInfo.FileName = item;
testInstance = Process.Start(testInfo);
testInstance.WaitForExit();
if (File.Exists("UnitTests.trx"))
{
resultsDoc = new XmlDocument();
resultsDoc.Load("UnitTests.trx");
foreach (XmlNode result in resultsDoc.GetElementsByTagName("UnitTestResult"))
{
// Get the execution ID for the test
execId = result.Attributes["executionId"].Value;
// Find the execution and test method nodes
executionNode = resultsDoc.GetElementsByTagName("Execution")
.OfType<XmlNode>()
.Where(n => n.Attributes["id"] != null && n.Attributes["id"].Value.Equals(execId))
.First();
testMethodNode = executionNode.ParentNode
.ChildNodes
.OfType<XmlNode>()
.Where(n => n.Name.Equals("TestMethod"))
.First();
// Get the class name, test name and result
className = testMethodNode.Attributes["className"].Value.Split('','')[0];
testName = result.Attributes["testName"].Value;
testResult = result.Attributes["outcome"].Value;
resultLine = String.Format("{0} {1}.{2}", testResult, className, testName);
results.Add(resultLine);
Console.WriteLine(resultLine);
}
File.Delete("UnitTests.trx");
}
}
// Calculate passed / failed test case count
int passed = results.Where(r => r.StartsWith("Passed")).Count();
int failed = results.Where(r => r.StartsWith("Failed")).Count();
// Print the summary
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine("-------");
Console.WriteLine("Test Run {0}", failed > 0 ? "Failed." : "Passed.");
Console.WriteLine();
if (passed > 0)
Console.WriteLine("/tPassed {0,7}", passed);
if (failed > 0)
Console.WriteLine("/tFailed {0,7}", failed);
Console.WriteLine("/t--------------");
Console.WriteLine("/tTotal {0,8}", results.Count);
if (failed > 0)
Environment.Exit(-1);
else
Environment.Exit(0);
Mi archivo App.config:
<appSettings>
<add key="ExcludedProjects" value="UnitTests.Bootstrap,UnitTests.Utils" />
<add key="MSTestCommand" value=""c:/Program Files (x86)/Microsoft Visual Studio 12.0/Common7/IDE/MSTest.exe" /testcontainer:"{0}/bin/Release/{1}.dll" /nologo /resultsfile:"UnitTests.trx"" />
</appSettings>
Solo para completar, a menudo quiero ejecutar pruebas como aplicación de consola, simplemente porque me parece mucho más fácil depurar esto por alguna razón ... A lo largo de los años he creado algunos ayudantes de prueba pequeños para ayudarme; Supongo que puedes usarlos con tu solución de CI con bastante facilidad.
Entiendo que esta no es tu pregunta por completo; Sin embargo, ya que está buscando una solución de CI y menciona Visual Studio, esto debería resolverlo muy bien.
Solo para que sepan, mi pequeño marco es un poco más grande que esto, pero las cosas que faltan son bastante fáciles de agregar. Básicamente, lo que omití es todo para el registro y el hecho de que probé diferentes ensamblajes en diferentes dominios de aplicaciones (debido a posibles conflictos y estado de DLL). Más sobre eso a continuación.
Una cosa a tener en cuenta es que no detecto ninguna excepción en el proceso a continuación. Mi objetivo principal es facilitar la depuración de la aplicación cuando se solucionan problemas. Tengo una implementación separada (pero similar) para CI que básicamente agrega intentos / capturas en los puntos de comentarios a continuación.
Este método solo tiene un problema: Visual Studio no copiará todos los ensamblajes a los que hace referencia; solo copiará los ensamblajes que utilice en el código. Una solución simple para esto es introducir un método (que nunca se llama) que use un tipo en la DLL que está probando. De esa manera, su ensamblaje se copiará y todo funcionará bien.
Aquí está el código:
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...
}
}
}
Cómo utilizar este código:
- Simplemente puede llamar a
TestHelpers.TestAll()
para probar todos los ensamblajes, los ensamblados referenciados, los ensamblados referenciados indirectamente, etc. Esto es probablemente lo que desea hacer en CI. - Puede llamar a
TestHelpers.TestAll(assembly)
para probar un solo ensamblaje con todos los ensamblajes referenciados. Esto puede ser útil cuando se dividen las pruebas en varios ensamblajes y / o cuando se realiza la depuración. - Puede llamar a
new MyObject().TestAll()
para invocar todas las pruebas en un solo objeto. Esto es particularmente útil cuando se depura.
Si está utilizando aplicaciones como yo, debe crear un único dominio de aplicación para una DLL que cargue dinámicamente desde una carpeta y usar TestAll en esa. Además, si usa una carpeta reutilizable, es posible que desee vaciarla entre las pruebas. De esa manera, las versiones múltiples de framework de prueba y las pruebas múltiples no interactuarán entre sí. En particular, si sus pruebas usan el estado (por ejemplo, variables estáticas), esto podría ser una buena práctica. Hay CreateInstanceAndUnwrap
ejemplos para CreateInstanceAndUnwrap
línea que te ayudarán con esto.
Una cosa a tener en cuenta es que uso un delegado en lugar del method.Invoke
. Básicamente, esto significa que su objeto de excepción no será devorado por Reflection, lo que significa que su depurador no se romperá. También tenga en cuenta que verifico los atributos por nombre, lo que significa que esto funcionará con diferentes marcos, siempre que los nombres de los atributos coincidan.
HTH