visual unit testmethod test studio net .net nunit

.net - testmethod - unit test c#



NUnit DeploymentItem (4)

En MsTest si necesito algún archivo de otro proyecto para mi prueba, puedo especificar el atributo DeploymentItem. ¿Hay algo similar en NUnit?


He realizado algunas mejoras en la solución de Alexander Pasha: le he dado al atributo la misma firma que a MSTest, de modo que el primer parámetro es el archivo o la carpeta absoluta o relativa a implementar, y el segundo parámetro opcional es el absoluto o ruta relativa a la que se va a desplegar. En ambos casos, "relativo" significa para el programa de ejecución. También he eliminado cualquier atributo de solo lectura del archivo desplegado. Esto es importante: si un archivo implementado anteriormente no se puede sobrescribir, el atributo se lanzará. También vale la pena señalar que MSTest y NUnit tienen estrategias muy diferentes a la hora de implementar los archivos que se utilizarán durante las pruebas. MSTest puede o no copiar archivos a una carpeta de implementación; consulte here . NUnit usa la propiedad ShadowCopyFiles del dominio de aplicación, que se implementa en una ubicación muy oscura en la carpeta temporal del usuario. Si bien las instantáneas se pueden activar y desactivar en NUnit, no sé cómo manipularlas cuando se utiliza Test Explorer en Visual Studio. En este sentido , es importante tener en cuenta que el adaptador de prueba de Visual Studio NUnit anterior a la versión 2 tiene la copia de la sombra activada, pero en la versión 2 en adelante está desactivada. Esto puede tener un gran impacto en las pruebas que utilizan elementos de implementación y vale la pena tenerlo en cuenta. Aquí está mi versión de DeploymentItemAttribute:

namespace NUnitDeploymentItem { [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] public class DeploymentItemAttribute : Attribute { /// <summary> /// NUnit replacement for Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute /// Marks an item to be relevant for a unit-test and copies it to deployment-directory for this unit-test. /// </summary> /// <param name="path">The relative or absolute path to the file or directory to deploy. The path is relative to the build output directory.</param> /// <param name="outputDirectory">The path of the directory to which the items are to be copied. It can be either absolute or relative to the deployment directory.</param> public DeploymentItemAttribute(string path, string outputDirectory = null) { // Escape input-path to correct back-slashes for Windows string filePath = path.Replace("/", "//"); // Look up where we are right now DirectoryInfo environmentDir = new DirectoryInfo(Environment.CurrentDirectory); // Get the full path and name of the deployment item string itemPath = new Uri(Path.Combine(environmentDir.FullName, filePath)).LocalPath; string itemName = Path.GetFileName(itemPath); // Get the target-path where to copy the deployment item to string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // NUnit uses an obscure ShadowCopyCache directory which can be hard to find, so let''s output it so the poor developer can get at it more easily Debug.WriteLine("DeploymentItem: Copying " + itemPath + " to " + binFolderPath); // Assemble the target path string itemPathInBin; if (string.IsNullOrEmpty(outputDirectory)) { itemPathInBin = new Uri(Path.Combine(binFolderPath, itemName)).LocalPath; } else if (!string.IsNullOrEmpty(Path.GetPathRoot(outputDirectory))) { itemPathInBin = new Uri(Path.Combine(outputDirectory, itemName)).LocalPath; } else { itemPathInBin = new Uri(Path.Combine(binFolderPath, outputDirectory, itemName)).LocalPath; } // Decide whether it''s a file or a folder if (File.Exists(itemPath)) // It''s a file { // Assemble the parent folder path (because the item might be in multiple sub-folders. string parentFolderPathInBin = new DirectoryInfo(itemPathInBin).Parent.FullName; // If the target directory does not exist, create it if (!Directory.Exists(parentFolderPathInBin)) { Directory.CreateDirectory(parentFolderPathInBin); } // copy source-file to the destination File.Copy(itemPath, itemPathInBin, true); // We must allow the destination file to be deletable FileAttributes fileAttributes = File.GetAttributes(itemPathInBin); if ((fileAttributes & FileAttributes.ReadOnly) != 0) { File.SetAttributes(itemPathInBin, fileAttributes & ~FileAttributes.ReadOnly); } } else if (Directory.Exists(itemPath)) // It''s a folder { // If it already exists, remove it if (Directory.Exists(itemPathInBin)) { Directory.Delete(itemPathInBin, true); } // Create target directory Directory.CreateDirectory(itemPathInBin); // Now Create all of the sub-directories foreach (string dirPath in Directory.GetDirectories(itemPath, "*", SearchOption.AllDirectories)) { Directory.CreateDirectory(dirPath.Replace(itemPath, itemPathInBin)); } //Copy all the files & Replace any files with the same name foreach (string sourcePath in Directory.GetFiles(itemPath, "*.*", SearchOption.AllDirectories)) { string destinationPath = sourcePath.Replace(itemPath, itemPathInBin); File.Copy(sourcePath, destinationPath, true); // We must allow the destination file to be deletable FileAttributes fileAttributes = File.GetAttributes(destinationPath); if ((fileAttributes & FileAttributes.ReadOnly) != 0) { File.SetAttributes(destinationPath, fileAttributes & ~FileAttributes.ReadOnly); } } } else { Debug.WriteLine("Warning: Deployment item does not exist - /"" + itemPath + "/""); } } } }


Probé la solución de implementar DeploymentItemAttribute , pero encontré un problema porque la clase se crearía una instancia cuando se cargaran las pruebas. Esto provocó que la lógica de implementación intentara ejecutarse cuando el adaptador de prueba NUnit de Visual Studio estaba cargando clases de prueba, en su fase de descubrimiento. Esto no es una gran idea

En su lugar, opté por implementar un método estático, ItemDeployment.DeployItems , para implementar elementos, al que puedes llamar al configurar tu dispositivo de prueba:

using System; using System.Collections.Generic; using System.IO; using System.Reflection; /// <summary> /// Logic for deploying items for tests. /// </summary> internal static class ItemDeployment { /// <summary> /// Call in subclass to deploy items before testing. /// </summary> /// <param name="items">Items to deploy, relative to project root.</param> /// <param name="retainDirectories">Retain directory structure of source items?</param> /// <exception cref="FileNotFoundException">A source item was not found.</exception> /// <exception cref="DirectoryNotFoundException">The target deployment directory was not found</exception> public static void DeployItems(IEnumerable<string> items, bool retainDirectories=false) { var environmentDir = new DirectoryInfo(Environment.CurrentDirectory); var binFolderPath = GetDeploymentDirectory(); foreach (var item in items) { if (string.IsNullOrWhiteSpace(item)) { continue; } string dirPath = retainDirectories ? Path.GetDirectoryName(item) : ""; var filePath = item.Replace("/", @"/"); var itemPath = new Uri(Path.Combine(environmentDir.Parent.Parent.FullName, filePath)).LocalPath; if (!File.Exists(itemPath)) { throw new FileNotFoundException(string.Format("Can''t find deployment source item ''{0}''", itemPath)); } if (!Directory.Exists(binFolderPath)) throw new DirectoryNotFoundException(string.Format("Deployment target directory doesn''t exist: ''{0}''", binFolderPath)); var dirPathInBin = Path.Combine(binFolderPath, dirPath); if (!Directory.Exists(dirPathInBin)) Directory.CreateDirectory(dirPathInBin); var itemPathInBin = new Uri(Path.Combine(binFolderPath, dirPath, Path.GetFileName(filePath))).LocalPath; if (File.Exists(itemPathInBin)) { File.Delete(itemPathInBin); } File.Copy(itemPath, itemPathInBin); } } /// <summary> /// Get directory test is deployed in. /// </summary> /// <returns></returns> public static string GetDeploymentDirectory() { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } }

Luego, en su dispositivo de prueba, puede implementar elementos para sus pruebas de la siguiente manera:

[TestFixture] public class TestDatabaseService { /// <summary> /// This is run once before any tests in this fixture. /// </summary> [TestFixtureSetUp] public void SetUpFixture() { ItemDeployment.DeployItems(new[] { @"App_Data/database.mdf" }); } }


Recogí la solución de @Matthew, la limpié un poco y la extendí para admitir múltiples usos de atributos para una prueba, y directorios completos que se pueden usar como DeploymentItems (incluidos los directorios que contienen subdirectorios).

namespace NUnitDeploymentItem { using System; using System.IO; using System.Reflection; [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] public class DeploymentItem : Attribute { /// <summary> /// Marks an item to be relevant for a unit-test and copies it to deployment-directory for this unit-test. /// </summary> /// <param name="fileProjectRelativePath">The project-relative path to a file or a folder that will be copied into the deployment-directory of this unit-test.</param> public DeploymentItem(string fileProjectRelativePath) { // Escape input-path to correct back-slashes for Windows string filePath = fileProjectRelativePath.Replace("/", "//"); // Look up, where we are right now DirectoryInfo environmentDir = new DirectoryInfo(Environment.CurrentDirectory); // Get the full item-path of the deployment item string itemPath = new Uri(Path.Combine(environmentDir.Parent.Parent.FullName, filePath)).LocalPath; // Get the target-path where to copy the deployment item to string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Assemble the target path string itemPathInBin = new Uri(Path.Combine(binFolderPath, filePath)).LocalPath; // Decide whether it''s a file or a folder if (File.Exists(itemPath)) // It''s a file { // If it already exists, remove it if (File.Exists(itemPathInBin)) { File.Delete(itemPathInBin); } // Assemble the parent folder path (because the item might be in multiple sub-folders. string parentFolderPathInBin = new DirectoryInfo(itemPathInBin).Parent.FullName; // If the target directory does not exist, create it if (!Directory.Exists(parentFolderPathInBin)) { Directory.CreateDirectory(parentFolderPathInBin); } // If the source-file exists, copy it to the destination if (File.Exists(itemPath)) { File.Copy(itemPath, itemPathInBin); } } else if (Directory.Exists(itemPath)) // It''s a folder { // If it already exists, remove it if (Directory.Exists(itemPathInBin)) { Directory.Delete(itemPathInBin, true); } // If the source-directory exists, copy it to the destination if (Directory.Exists(itemPath)) { // Create target directory Directory.CreateDirectory(itemPathInBin); // Now Create all of the sub-directories foreach (string dirPath in Directory.GetDirectories(itemPath, "*", SearchOption.AllDirectories)) { Directory.CreateDirectory(dirPath.Replace(itemPath, itemPathInBin)); } //Copy all the files & Replaces any files with the same name foreach (string newPath in Directory.GetFiles(itemPath, "*.*", SearchOption.AllDirectories)) { File.Copy(newPath, newPath.Replace(itemPath, itemPathInBin), true); } } } } } }

Esta es realmente una solución que se construyó a partir de las respuestas a estas preguntas: verifique si la Ruta es un archivo o directorio , Copie todo el contenido de un directorio y Cree un archivo si la carpeta de destino no existe .


Deberías revisar otro hilo que contrasta las capacidades de NUnit y MSTest .

La respuesta aceptada aquí es engañosa. NUnit no ofrece el atributo [DeploymentItem ("")], que es lo que @Idsa quería para una solución equivalente en NUnit.

Mi conjetura es que este tipo de atributo violaría el alcance de NUnit como un marco de prueba de "unidad" ya que requiere que un elemento se copie a la salida antes de ejecutar una prueba implica que tiene una dependencia de que este recurso esté disponible.

Estoy usando un atributo personalizado para copiar sobre una instancia de db local para ejecutar pruebas de "unidad" contra algunos datos de prueba considerables que preferiría no generar con el código cada vez.

Ahora, utilizando el atributo [DeploymentItem ("some / project / file")] copiaré este recurso del sistema de archivos en el contenedor de nuevo, actualizando efectivamente mis datos de origen por método de prueba:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] public class DeploymentItem : System.Attribute { private readonly string _itemPath; private readonly string _filePath; private readonly string _binFolderPath; private readonly string _itemPathInBin; private readonly DirectoryInfo _environmentDir; private readonly Uri _itemPathUri; private readonly Uri _itemPathInBinUri; public DeploymentItem(string fileProjectRelativePath) { _filePath = fileProjectRelativePath.Replace("/", @"/"); _environmentDir = new DirectoryInfo(Environment.CurrentDirectory); _itemPathUri = new Uri(Path.Combine(_environmentDir.Parent.Parent.FullName , _filePath)); _itemPath = _itemPathUri.LocalPath; _binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); _itemPathInBinUri = new Uri(Path.Combine(_binFolderPath, _filePath)); _itemPathInBin = _itemPathInBinUri.LocalPath; if (File.Exists(_itemPathInBin)) { File.Delete(_itemPathInBin); } if (File.Exists(_itemPath)) { File.Copy(_itemPath, _itemPathInBin); } } }

Entonces podemos usarlo así:

[Test] [DeploymentItem("Data/localdb.mdf")] public void Test_ReturnsTrue() { Assert.IsTrue(true); }