带有第三方 dll 的 VisualStudio NUnit3TestAdapter 测试项目

VisualStudio NUnit3TestAdapter testing project with third party dlls

我有一个带有两个 nuget 包的测试项目 Proj_Test

<packages>
  <package id="NUnit" version="3.6.0" targetFramework="net45" />
  <package id="NUnit3TestAdapter" version="3.6.0" targetFramework="net45" />
</packages>

Proj_Test 引用了测试项目 Proj

Proj 引用了其他几个需要加载的 dll。 我在哪里可以添加此信息,以便我可以从 IDE 中使用 NUnit3TestAdapter 开始测试,而无需实际将 dll 复制到输出文件夹。

a solution for the Nunit2 Runners。但是当我试图通过 NUnit3TestAdapter 将它用于 Nunit3 时我失败了。

根据 tips and tricks section 我通过菜单添加了一个设置文件 Test.runsettings

<RunSettings>
  <NUnit>
    <PrivateBinPath>D:\Drive\AnyThirdParty</PrivateBinPath>
  </NUnit>
</RunSettings>

设置似乎被忽略了。

如何为我的测试管理这些依赖项?

编辑: This is我觉得发生了什么事。

Private assemblies are deployed in the same directory structure as the application. If the directories specified for PrivateBinPath are not under ApplicationBase, they are ignored.

创建副本真的是唯一的解决办法吗?

如果找不到更好的,请尝试自己解决

using ConsoleApplication6;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Reflection;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestInitialize]
        public void Init()
        {
           AppDomain currentDomain = AppDomain.CurrentDomain;
           currentDomain.AssemblyResolve += MyResolveEventHandler;
        }

        [TestMethod]
        public void TestMethod1() { Assert.AreEqual(new MyClass().DoSomething(), 1); }

        [TestMethod]
        public void TestMethod2() { Assert.AreEqual(new MyClass().DoSomething(), 1); }

        private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
        {
            return Assembly.LoadFile(@"C:\MyPath\MyAssembly.dll");
        }
    }
}

遗憾的是,程序集探测仅适用于子目录,因此您不能使用它...

感谢 George Vovos 的回答,这就是我最终实现的。

using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

//https://github.com/nunit/docs/wiki/SetUpFixture-Attribute
//A SetUpFixture outside of any namespace provides SetUp and TearDown for the entire assembly.

[SetUpFixture]
class GlobalSetup
{

    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int SetDllDirectory(string NewDirectory);

    static HashSet<string> directories = new HashSet<string>
    {
        @"D:\Drive\AnyThirdParty\"
    };

    [OneTimeSetUp]
    public void RunBeforeAnyTests()
    {
        AddManagedHandler();
        SetNativeDirectories();
    }

    private void SetNativeDirectories()
    {
        if(directories.Count() != 1)
        {
            //TODO: add support for multiple directories
            throw new NotImplementedException("current implementation only supports exactly one directory");
        }

        if (0 == SetDllDirectory(directories.First()))
        {
            throw new Exception("SetDllDirectory failed with error " + Marshal.GetLastWin32Error());
        }
    }

    private void AddManagedHandler()
    {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        IEnumerable<string> candidates = FindCandidates(new AssemblyName(args.Name));
        return Assembly.LoadFrom(candidates.First());
    }

    private static IEnumerable<string> FindCandidates(AssemblyName assemblyname)
    {
        List<string> candidates = new List<string>();
        foreach (var path in directories)
        {
            string candidate = string.Format(@"{0}{1}.dll", path, assemblyname.Name);
            if (File.Exists(candidate))
            {
                candidates.Add(candidate);
            }
        }
        if (!candidates.Any())
        {
            throw new FileNotFoundException(string.Format("Can not find assembly: '{0}.dll'", assemblyname.Name));
        }
        return candidates;
    }
}