NSubstitute.Substitute.For <t>()引发FileNotFound异常,但仅在Visual Studio上使用MSTest

时间:2018-08-28 14:54:41

标签: c# mstest nsubstitute

我们在NSubstitute中遇到了问题,即它找不到某些DLL。似乎问题来自于来自预编译DLL的模拟类型引用来自其他预编译DLL的类型。因此,我们有类似A.dll的B.dll引用类型,也引用了C.dll的类型。有一个异常告诉我们,即使C.dll在bin文件夹中并且在项目中正确引用,也无法找到。所有版本也都匹配。通过删除对C dll的引用,我设法通过了一些测试。我还签出了预编译的解决方案,并且DLL版本一致。在项目中所有的掘金都是最新的。所有构建体系结构,发行版和语言版本也保持一致。

使这个问题更奇怪的是,在我们的持续集成服务器中使用VSTest.Console不会重现该问题,并且我没有看到运行的测试数量有所减少,而只是使用了VS Test Explorer。 ReSharper的测试资源管理器也没有此问题。升级到MSTest v2时,我们看到了这个问题,以前的版本可以工作。

这是堆栈跟踪(我截断了路径和名称空间):

Result StackTrace:  
at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
   at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
   at System.Reflection.Emit.TypeBuilder.CreateTypeInfo()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.CreateType(TypeBuilder type)
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
   at Castle.DynamicProxy.Generators.InterfaceProxyWithoutTargetGenerator.GenerateType(String typeName, Type proxyTargetType, Type[] interfaces, INamingScope namingScope)
   at Castle.DynamicProxy.Generators.InterfaceProxyWithTargetGenerator.<>c__DisplayClass6_0.<GenerateCode>b__0(String n, INamingScope s)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory)
   at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors)
   at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments)
   at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments, SubstituteConfig config)
   at NSubstitute.Substitute.For[T](Object[] constructorArguments)
   at OurTests.Fixtures.MockBuilder.Build() in C:\MockBuilder.cs:line 10
   at UnitTests.TestInitialize()

TestCleanup Stack Trace
   at Tests.TestCleanup() in 
Result Message: 
Initialization method Tests.TestInitialize threw exception. System.IO.FileNotFoundException: Could not load file or assembly 'AssemblyC, Version=1, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified..

TestCleanup method Tests.TestCleanup threw exception. System.NullReferenceException: System.NullReferenceException: Object reference not set to an instance of an object..

请注意,在升级到MSTest V2时,即使该路径是绝对路径,它也将运行我们的DLL的调试版本而不是指定的发行版本时遇到问题。我不知道这是否相关。

1 个答案:

答案 0 :(得分:0)

在削减精神健康预算两天后,我终于找到了解决方法。还是一个补丁,因为我仍然不能100%确定原因。

[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
    AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveEventHandler;
}

private static Assembly AssemblyResolveEventHandler(object sender, ResolveEventArgs args)
{
    var dllName = args.Name.Split(',')[0] + ".dll";
    var assemblyPath = Path.Combine(BinDir, dllName);
    return Assembly.LoadFile(assemblyPath);
}

请注意,这可能应该在AssemblyInitialize方法中。

我最好的猜测是,在TestInitialize方法中,它没有对DLL进行显式调用,因此此时它们不会被加载,而Castle(NSubstitute使用的)显然还不了解它们,并且可能尝试从当前已知的程序集加载。但是,当我们从连续集成脚本运行测试时,之前已经进行了其他测试,因此DLL已经可用(也许是Global Assembly Cache?)。至于ReSharper的测试运行程序为何起作用,我不知道,我想他们是在他们的产品中投入了很多开发人员的力量。

此外,应归功于应得的信用:https://stackoverflow.com/a/8967026/4602726