Миграция в MSTest.TestFramework: AppDomain и CreateInstanceAndUnwrap, приведение не выполнено - PullRequest
0 голосов
/ 29 ноября 2018

Я должен оценить миграцию кодовой базы (тысячи тестов с общим вспомогательным кодом, используемым несколькими командами) из Visual Studio 2015 и "Microsoft.VisualStudio.QualityTools.UnitTestFramework" в Visual Studio 2017 и новую "MSTest.TestFramework".

Некоторые тесты выполняют код в другом домене приложений (чтобы выгрузить некоторые библиотеки 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.

FUSLOGVW всегда говорит «Assembly is loaded in default load context» как при загрузке из каталога «Out», так и при загрузке из «bin \ Debug».Во второй раз он также говорит:

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 - это каталог "Out" для обоих доменов!

(Кстати, я заметил, что со старымframework LoaderOptimization в SetupInformation установлен на DomainMask, в то время как в новом Framework установлен на NotSpecified. Я пытался использовать эту опцию при вызове CreateDomain, но ничего не меняется)

Iпопытался заменить "type.Assembly.FullName" на "type.Assembly.Location", я попытался добавить обработчик к AssemblyResolve (как в основном AppDomain, так и в пользовательском AppDomain; пробовал разные комбинации Load / LoadFrom), но ни один из них не работает.

Конечно, если я избавлюсь от атрибута DeploymentItem, он будет работать, потому что он не копирует библиотеки DLL во временный каталог «Out» (он запускает тесты непосредственно из «bin \ Debug»).Но поскольку кодовая база велика, я не уверен, что смогу удалить ее повсюду, я бы не хотел трогать код, принадлежащий другим командам.Кроме того, если пользователь (или существующая автоматическая процедура, такая как выпуск TFS) по-прежнему имеет файл .runsettings с включенным развертыванием по какой-либо причине, библиотеки DLL все равно будут скопированы и тесты не пройдут.

В некоторых статьях упоминается использование dynamic вместо приведения для преодоления проблемы, но это также предполагает изменение множества тестов.Во всяком случае, я попытался dynamic, и это позже не удается при попытке вызвать метод

Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException: произошло непредвиденное исключение при привязке динамической операции

Есть ли другой способ обойти проблему?

Примечания:

1. Проблема также возникает с версией 1.4 TestFramework / TestAdapter иверсия 16.0.0-preview-20181128-01 для Microsoft.NET.Test.Sdk.

2. если я передаю AppDomainInitializer CreateDomain, и он содержит "new SpecialTestCode()", объект создан из правильной сборки, из" Out ".Но я не знаю, как выставить этот объект за пределами AppDomain.

3. Если экземпляр класса, то есть SpecialTestCode, находится в другой DLL, он работает.Другая сборка корректно загружается из каталога «Out».

4. Коллега предложил мне опубликовать это как выпуск на странице GitHub MsTest .

...