NSubstitute.Substitute.For <T>() создает исключение FileNotFound, но только с использованием MSTest в Visual Studio - PullRequest
0 голосов
/ 28 августа 2018

У нас есть проблемы с NSubstitute, когда он не может найти определенные библиотеки DLL. Похоже, что проблема возникает с ложными типами из предварительно скомпилированных библиотек DLL, ссылающихся на типы из других предварительно скомпилированных библиотек DLL. Итак, у нас есть что-то вроде A.dll, ссылающееся на типы из B.dll, которое также ссылается на типы из C.dll. Есть исключение, говорящее нам, что C.dll не может быть найден, даже если он находится в папке bin и правильно указан в проектах. Все версии также совпадают. Мне удалось пройти несколько тестов, удалив ссылки на Cll. Я также извлекаю решения для предварительно скомпилированных версий 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 голосов
/ 28 августа 2018

После двух дней сокращения бюджета на психическое здоровье я наконец нашел решение. Вернее, патч, так как я до сих пор не на 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

...