在MsTest中,如果我需要来自另一个项目的某个文件进行测试,我可以指定DeploymentItem属性。 NUnit中有类似的内容吗?
答案 0 :(得分:24)
您应该查看contrasts the capabilities of NUnit and MSTest。
的另一个帖子这里接受的答案具有误导性。 NUnit根本不提供[DeploymentItem(“”)]属性,这是@Idsa想要在NUnit中使用的等效解决方案。
我的猜测是,这种属性会违反NUnit作为“单元”测试框架的范围,因为要求在运行测试之前将项目复制到输出,这意味着它依赖于此资源可用。 / p>
我正在使用自定义属性来复制localdb实例,以便针对某些相当大的测试数据运行“单元”测试,而我每次都不会使用代码生成这些测试数据。
现在使用属性[DeploymentItem(“some / project / file”)]会将此资源从文件系统复制到bin中,再次按照测试方法有效刷新源数据:
[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);
}
}
}
然后我们可以这样使用:
[Test]
[DeploymentItem("Data/localdb.mdf")]
public void Test_ReturnsTrue()
{
Assert.IsTrue(true);
}
答案 1 :(得分:4)
我对Alexander Pasha的解决方案做了一些改进:我给了属性与MSTest属性相同的签名,这样第一个参数就是绝对或相对文件或文件夹部署,可选的第二个参数是要部署它的绝对路径或相对路径。在这两种情况下,相对'意味着执行程序。我还从已部署的文件中删除了任何只读属性。这很重要 - 如果以前部署的文件无法覆盖,则属性将被抛出。 值得注意的是,MSTest和NUnit在部署测试期间使用的文件时采用了截然不同的策略。 MSTest 可能或可能将文件复制到部署文件夹 - 请参阅here。 NUnit使用AppDomain的ShadowCopyFiles属性,该属性部署到用户临时文件夹中非常模糊的位置。虽然可以在NUnit中打开和关闭卷影复制,但在Visual Studio中使用Test Explorer时我不知道如何操作它。在这方面需要注意,版本2之前的Visual Studio NUnit测试适配器已启用阴影复制,但在版本2之后,它已关闭。这可能会对使用部署项的测试产生重大影响,值得深入了解。 这是我的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 + "\"");
}
}
}
}
答案 2 :(得分:3)
我从@Matthew中选择了解决方案并将其扩展为支持多个属性用于一个测试,以及整个目录可用作DeploymentItems(包括包含子目录的目录)。< / p>
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);
}
}
}
}
}
}
这实际上是根据这些问题的答案构建的解决方案:Check if Path is a file or directory,Copy entire content of a directory和Create file if target folder does not exist。
答案 3 :(得分:2)
我尝试了实现DeploymentItemAttribute
的解决方案,但发现它有问题,因为在加载测试时会对类进行实例化。这导致部署逻辑尝试执行,因为Visual Studio NUnit测试适配器正在其发现阶段加载测试类。这不是一个好主意。
我选择实施静态方法ItemDeployment.DeployItems
来部署项目,您可以在设置测试夹具时调用这些方法:
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);
}
}
然后,在您的测试夹具中,您可以为测试部署项目,如下所示:
[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" });
}
}
答案 4 :(得分:1)
这是一个使用TestContext类与NUnit 3+一起使用的解决方案。测试
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct,
AllowMultiple = false,
Inherited = false)]
public class DeploymentItem : System.Attribute {
public DeploymentItem(string relativeFilePathToCopy) {
relativeFilePathToCopy = EscapeFilePathSlashes(relativeFilePathToCopy);
var filePathToCopy = GeneratePathForFileToCopy(relativeFilePathToCopy);
var destinationFilePath = new Uri(Path.Combine(TestContext.CurrentContext.WorkDirectory, relativeFilePathToCopy)).LocalPath;
DeleteFileIfItAlreadyExistsInTargetDirectory(destinationFilePath);
CopyFileFromProjectToTestWorkingDirectory(filePathToCopy, destinationFilePath);
}
private static string EscapeFilePathSlashes(string relativeFilePathToCopy)
{
relativeFilePathToCopy = relativeFilePathToCopy.Replace("/", @"\");
return relativeFilePathToCopy;
}
private static string GeneratePathForFileToCopy(string relativeFilePathToCopy)
{
var rootOfTestProject = new DirectoryInfo(TestContext.CurrentContext.TestDirectory).Parent.Parent.FullName;
var filePathToCopy = new Uri(Path.Combine(rootOfTestProject, relativeFilePathToCopy)).LocalPath;
return filePathToCopy;
}
private static void CopyFileFromProjectToTestWorkingDirectory(string pathToFileInProject, string targetUriForFile)
{
if (File.Exists(pathToFileInProject))
{
File.Copy(pathToFileInProject, targetUriForFile);
}
}
private static void DeleteFileIfItAlreadyExistsInTargetDirectory(string targetUriForFile)
{
if (File.Exists(targetUriForFile))
{
File.Delete(targetUriForFile);
}
}
}