迁移到MSTest.TestFramework:AppDomain和CreateInstanceAndUnwrap,强制转换失败

时间:2018-11-29 13:16:13

标签: c# visual-studio-2017 mstest appdomain

我必须评估从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

0 个答案:

没有答案