NUnit DeploymentItem - PullRequest
       0

NUnit DeploymentItem

24 голосов
/ 21 февраля 2012

В MsTest, если мне нужен какой-то файл из другого проекта для моего теста, я могу указать атрибут DeploymentItem.Есть ли что-нибудь подобное в NUnit?

Ответы [ 4 ]

24 голосов
/ 20 июля 2012

Вы должны проверить другой поток, который противопоставляет возможности NUnit и MSTest .

Принятый ответ здесь вводит в заблуждение.NUnit вообще не предлагает атрибут [DeploymentItem ("")], для которого @Idsa хотела найти эквивалентное решение в NUnit.

Я предполагаю, что этот вид атрибута будет нарушать область действия NUnit как "модульной" среды тестирования, так как требование копирования элемента в вывод перед запуском теста подразумевает, что он зависит от этого ресурса.доступный.

Я использую пользовательский атрибут для копирования экземпляра localdb для выполнения «модульных» тестов с некоторыми значительными тестовыми данными, которые я не хотел бы генерировать с кодом каждый раз.

Теперь, используя атрибут [DeploymentItem ("some / project / file")], скопируем этот ресурс из файловой системы в корзину снова, эффективно обновляя мои исходные данные для каждого метода теста:

[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);
}
4 голосов
/ 13 апреля 2015

Я сделал несколько улучшений в решении Александра Паша: я присвоил атрибуту ту же сигнатуру, что и у MSTest, так что первый параметр - это абсолютный или относительный файл или папка для развертывания, а необязательный второй параметр. это абсолютный или относительный путь, по которому он должен быть развернут. В обоих случаях «относительный» означает выполнение программы. Также я удалил любой атрибут только для чтения из развернутого файла. Это важно - если ранее развернутый файл не может быть перезаписан, атрибут выбросит. Также стоит отметить, что MSTest и NUnit имеют очень разные стратегии, когда речь идет о развертывании файлов, которые будут использоваться во время тестирования. MSTest может или не может копировать файлы в папку развертывания - см. здесь . NUnit использует свойство ShadowCopyFiles AppDomain, которое развертывается в очень непонятном месте во временной папке пользователя. Хотя теневое копирование можно включать и выключать в самом NUnit, я не знаю, как им манипулировать при использовании Test Explorer в Visual Studio. В связи с этим важно отметить , что в тестовом адаптере Visual Studio NUnit до версии 2 включено теневое копирование, а в версии 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 + "\"");
            }
        }
    }
}
3 голосов
/ 20 мая 2014

Я выбрал решение у @Matthew, немного его почистил и расширил, чтобы он поддерживал многократное использование атрибутов для одного теста, а также целые каталоги, которые можно использовать как DeploymentItems (включая каталоги, которые содержат подкаталоги).

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);
                    }
                }
            }
        }
    }
}

На самом деле это решение, построенное на основе ответов на следующие вопросы: Проверьте, является ли путь файлом или каталогом , Скопируйте все содержимое каталога и Создать файл если целевая папка не существует .

2 голосов
/ 06 февраля 2013

Я опробовал решение по реализации 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" });
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...