我必须评估从Visual Studio 2015和“ Microsoft.VisualStudio.QualityTools.UnitTestFramework
”到Visual Studio 2017和新的“ MSTest.TestFramework
”的代码库(成千上万的测试,以及多个团队使用的通用帮助程序代码)的迁移”。
某些测试在不同的AppDomain中执行代码(为了卸载某些DLL,长话短说):它们不再起作用。这是一个重现该问题的简化示例。
CSPROJ:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
<RootNamespace>MyCompany.MyTestPrj</RootNamespace>
<AssemblyName>MyCompany.MyTestPrj</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<Prefer32Bit>false</Prefer32Bit>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
<Prefer32Bit>false</Prefer32Bit>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Content Include="TextFile1.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
</ItemGroup>
</Project>
C#代码:
namespace MyCompany.MyTestPrj
{
[DeploymentItem("TextFile1.txt")]
[TestClass]
public class TestClass1
{
[TestMethod]
public void TestMethod1()
{
var setupInfo = AppDomain.CurrentDomain.SetupInformation;
AppDomain domain = AppDomain.CreateDomain("MY_" + Guid.NewGuid(), null, setupInfo);
Type type = typeof(SpecialTestCode);
Console.WriteLine($"Typeof, Location: {type.Assembly.Location}");
object proxy = domain.CreateInstanceAndUnwrap(
type.Assembly.FullName, type.FullName);
Console.WriteLine($"proxy, Location: {proxy.GetType().Assembly.Location}");
SpecialTestCode codeInOtherDomain = (SpecialTestCode)proxy;
codeInOtherDomain.SomeMethod();
}
}
public class SpecialTestCode : MarshalByRefObject
{
public SpecialTestCode()
{
// do something
}
public override object InitializeLifetimeService()
{
return null;
}
public void SomeMethod()
{
// Do something
}
}
}
它过去曾与“ Microsoft.VisualStudio.QualityTools.UnitTestFramework
”一起使用。使用“ MSTest.TestFramework
”时,强制转换失败,并显示
“ System.InvalidCastException:无法强制转换透明代理以键入'MyCompany.MyTestPrj.SpecialTestCode ”。
使用旧框架的控制台输出为:
Typeof, Location: C:\temp\MyTestPrj\TestResults\Deploy_myusername 2018-11-29 13_29_04\Out\MyCompany.MyTestPrj.dll
proxy, Location: C:\temp\MyTestPrj\TestResults\Deploy_myusername 2018-11-29 13_29_04\Out\MyCompany.MyTestPrj.dll
现在有了新框架:
Typeof, Location: C:\temp\MyTestPrj\TestResults\Deploy_myusername 2018-11-29 13_31_08\Out\MyCompany.MyTestPrj.dll
proxy, Location: C:\temp\MyTestPrj\MyTestPrj\bin\Debug\MyCompany.MyTestPrj.dll
看起来像一个绑定上下文问题。我阅读了有关该主题的文章,例如
但是它们似乎都没有解释为什么在迁移到新的MSTest.TestFramework时会发生这种情况。
从“ Out”目录加载和从“ bin \ Debug”加载时,FUSLOGVW始终显示“ Assembly is loaded in default load context
”。第二次,它还说:
WRN: The same assembly was loaded into multiple contexts of an application domain:
WRN: Context: Default | Domain ID: 4 | Assembly Name: MyCompany.MyTestPrj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
WRN: Context: LoadFrom | Domain ID: 4 | Assembly Name: MyCompany.MyTestPrj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
从“ bin \ Debug”加载时,FUSLOGVW表示Appbase 是实际上是“ bin \ Debug”。但是我将SetupInformation
从一个域传递到另一个域,调试器说ApplicationBase
是两个域的“输出”目录!
(顺便说一句,我注意到在SetupInformation中将旧框架LoaderOptimization
设置为DomainMask
,而在新框架中将设置为NotSpecified
。调用CreateDomain时使用此选项,但没有任何变化)
我尝试将“ type.Assembly.FullName
”替换为“ type.Assembly.Location
”,我尝试向AssemblyResolve
添加处理程序(在主AppDomain和自定义AppDomain中;尝试不同的组合/ LoadFrom),但都无效。
当然,如果我摆脱了DeploymentItem
属性,它就会起作用,因为它不会将DLL复制到临时的“ Out”目录(它直接从“ bin \ Debug”运行测试)。但是由于代码库很大,所以我不确定是否可以从任何地方删除它,所以我希望不要接触属于其他团队的代码。此外,如果由于某种原因,用户(或现有的自动过程,例如TFS发行版)仍具有启用了部署的.runsettings文件,则DLL仍将被复制并且测试将失败。
有些文章提到使用dynamic
而不是强制转换来克服该问题,但是它还涉及更改许多测试。无论如何,我尝试了dynamic
,但稍后尝试调用方法时失败了
Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException:绑定动态操作时发生了意外的异常
注释:
1.该问题也发生在TestFramework / TestAdapter的1.4版本和Microsoft.NET.Test.Sdk的16.0.0-preview-20181128-01版本中。
2.如果我将AppDomainInitializer
传递给CreateDomain,并且它包含“ new SpecialTestCode()
”,则该对象是从正确的程序集创建的,即“ Out”中的那个。但是我不知道如何在AppDomain之外公开此对象。
3.如果实例化的类(即SpecialTestCode)位于其他DLL中,则它可以工作。另一个程序集已从“ Out”目录正确加载。
4.一位同事建议我将其发布为问题on the GitHub page of MsTest。