Исправление ошибок IIS Express в CI с покрытием кода
Решения для устранения неудачных интеграционных тестов IIS Express в CI при включенном покрытии. Ошибки COM, отсутствие Shim DLL и альтернативные стратегии покрытия для .NET Framework.
Как решить проблемы с интеграционными тестами IIS Express в CI‑pipeline, когда включено покрытие кода для приложений .NET Framework?
Мои интеграционные тесты .NET работают локально, но в CI падают, когда включено покрытие кода. Тесты запускают несколько экземпляров IIS Express (ASP.NET, WebAPI, WCF), которые выполняют веб‑запросы к тестируемым приложениям.
В CI IIS Express не запускается и выдаёт ошибки вида:
[FileNotFoundException: Could not load file or assembly 'Microsoft.VisualStudio.CodeCoverage.Shim, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a']
После добавления отсутствующего shim‑DLL я получаю ошибки, связанные с COM:
[COMException (0x80040154): Retrieving the COM class factory for component with CLSID {A9E69610-B80D-11D0-B9B9-00A0C922E750} failed due to the following error: 80040154 Class not registered]
Я пробовал установить CollectAspDotNet в false в файле runsettings, но безуспешно. Мой текущий подход – установить IIS Express в CI, но это выглядит проблематичным из‑за зависимостей от COM‑компонентов.
Какое правильное решение, чтобы запускать интеграционные тесты .NET Framework с покрытием кода в CI без использования COM‑компонентов IIS Express?
IIS Express интеграционные тесты не проходят в CI‑конвейерах, когда включён сбор покрытия кода, главным образом из‑за отсутствующих зависимостей и проблем с регистрацией COM‑компонентов, которые автоматически решаются в локальной среде разработки, но не в CI‑системах. Основная проблема заключается в отсутствии сборки Microsoft.VisualStudio.CodeCoverage.Shim в CI‑окружениях, а также в зависимости IIS Express от COM‑компонентов, которые не зарегистрированы в большинстве CI‑систем.
Содержание
- [Понимание корневых причин](#понимание-корневых-ц причин)
- Немедленные решения для отсутствующего Shim DLL
- Устранение проблем с регистрацией COM
- Альтернативные стратегии покрытия
- Лучшие практики конфигурации RunSettings
- Рекомендации по настройке CI‑окружения
- Долгосрочный путь миграции
Понимание корневых причин
Ошибка, которую вы наблюдаете, связана с несколькими взаимосвязанными проблемами:
Зависимость от Code Coverage Shim: При включении покрытия кода через Visual Studio Test Platform (vstest.console.exe) запускается агент профилирования, которому требуется сборка Microsoft.VisualStudio.CodeCoverage.Shim.dll. Эта DLL содержит методы выполнения, необходимые для сбора данных о покрытии, но, как подтверждают issues Microsoft на GitHub, эта сборка не развертывается автоматически в дочерних процессах в CI‑окружениях.
Регистрация COM‑компонентов: IIS Express использует COM‑компоненты для работы, особенно для хостинга веб‑приложений. Ошибка CLSID {A9E69610-B80D-11D0-B9B9-00A0C922E750} указывает на то, что требуемый COM‑класс не зарегистрирован. Это происходит, потому что CI‑окружения обычно не регистрируют эти компоненты автоматически.
Проброс переменных среды: Исследования из репозитория vstest Microsoft показывают, что при включении покрытия кода переменные среды, включающие профилирование, передаются дочерним процессам, но Shim DLL не доступна для них, что приводит к FileNotFoundException.
Немедленные решения для отсутствующего Shim DLL
Ручной развертывание Shim DLL
Самый быстрый способ – вручную развернуть Shim DLL в CI‑конвейер:
-
Найдите Shim DLL: Найдите
Microsoft.VisualStudio.CodeCoverage.Shim.dllв установке Visual Studio, обычно в:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\CodeCoverage\15.0.0\Tools -
Разверните в CI: Добавьте DLL в каталог выполнения тестов CI или убедитесь, что она находится в PATH:
yaml# Пример Azure DevOps - task: UseDotNet@2 inputs: version: '5.0.x' includeVersion: 'true' performMultiLevelLookup: 'true' packageType: 'sdk' - script: | mkdir -p tools cp "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/Common7/IDE/Extensions/Microsoft/CodeCoverage/15.0.0/Tools/Microsoft.VisualStudio.CodeCoverage.Shim.dll" tools/ displayName: 'Copy Shim DLL to CI'
Установка переменных среды
Настройте необходимые переменные среды, чтобы тестовый раннер мог найти Shim DLL:
- script: |
set CODE_COVERAGE_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\CodeCoverage\15.0.0\Tools
set PATH=%CODE_COVERAGE_PATH%;%PATH%
displayName: 'Set Code Coverage Environment Variables'
Устранение проблем с регистрацией COM
Поскольку COM‑компоненты IIS Express не зарегистрированы в CI‑окружениях, у вас есть несколько вариантов:
Вариант 1: Использовать компоненты Windows Authentication
Регистрируйте необходимые COM‑компоненты вручную в CI‑конвейере:
# Запустите в скрипте CI для регистрации COM‑компонентов IIS Express
regsvr32 /s "C:\Program Files (x86)\IIS Express\iisexpress.exe"
Вариант 2: Использовать Kestrel вместо IIS Express
Для приложений .NET Core рассмотрите миграцию на хостинг Kestrel:
// В конфигурации теста используйте Kestrel вместо IIS Express
[AssemblyInitialize]
public static void Initialize(TestContext context)
{
var builder = new WebHostBuilder()
.UseStartup<Startup>()
.UseKestrel()
.UseUrls("http://localhost:5000");
var host = builder.Build();
host.Start();
}
Вариант 3: Использовать IIS вместо IIS Express
Если ваш CI‑окружение поддерживает полноценный IIS:
- task: IISWebAppDeploymentOnMachineGroup@0
inputs:
webSiteName: 'Default Web Site'
package: $(Build.ArtifactStagingDirectory)/drop/*.zip
displayName: 'Deploy to IIS'
Альтернативные стратегии покрытия
Использовать OpenCover вместо встроенного покрытия
OpenCover – инструмент покрытия кода .NET, который лучше работает с IIS Express в CI‑окружениях:
<!-- Пример OpenCover -->
<Target Name="RunTests">
<Exec Command="OpenCover.Console.exe -register:user -target:vstest.console.exe -targetargs:"IntegrationTests.dll /EnableCodeCoverage" -output:coverage.xml" />
</Target>
Использовать Coverlet для .NET Core приложений
Для проектов .NET Core Coverlet обеспечивает отличное покрытие без зависимостей от IIS:
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Используйте следующий runsettings:
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Coverlet"
codeCoverageAssemblyPath="coverlet.core.dll">
<Configuration>
<Format>opencover</Format>
<Include>[YourAssemblyName]*</Include>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
Лучшие практики конфигурации RunSettings
Оптимизированный RunSettings для CI
Создайте специализированный файл runsettings для CI‑окружений:
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage"
uri="datacollector://Microsoft/CodeCoverage/2.0"
assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CodeCoverage>
<!-- Использовать проверяемую инструментацию для лучшей совместимости -->
<UseVerifiableInstrumentation>true</UseVerifiableInstrumentation>
<!-- Позволить процессы с низким уровнем целостности (часто в CI) -->
<AllowLowIntegrityProcesses>true</AllowLowIntegrityProcesses>
<!-- Собирать данные из дочерних процессов, критичных для IIS Express -->
<CollectFromChildProcesses>true</CollectFromChildProcesses>
<!-- При необходимости отключить сбор ASP.NET -->
<CollectAspDotNet>false</CollectAspDotNet>
<!-- Добавить конкретные модули для покрытия -->
<ModulePaths>
<ModulePath cover="true" path=".*YourAssembly\.dll$"/>
</ModulePaths>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
<!-- Настройки выполнения тестов для CI -->
<TestRunParameters>
<Parameter name="Environment" value="CI" />
</TestRunParameters>
</RunSettings>
Условная конфигурация
Используйте условные символы компиляции для различий между CI и локальной средой:
#if CI
// Упрощенная настройка тестов для CI
var builder = new WebHostBuilder()
.UseStartup<Startup>()
.UseUrls("http://localhost:5000");
#else
// Полная настройка IIS Express для локальной разработки
var builder = new WebHostBuilder()
.UseStartup<Startup>()
.UseIISIntegration();
#endif
Рекомендации по настройке CI‑окружения
Требования к агенту Windows CI
Убедитесь, что ваш агент CI соответствует следующим требованиям:
- Visual Studio Build Tools: Установите Visual Studio Build Tools с компонентами Code Coverage
- IIS Express: Установите IIS Express с правильной регистрацией COM
- .NET Framework Runtime: Требуемые версии, соответствующие вашему приложению
- PowerShell: Для выполнения скриптов регистрации
Пример конвейера Azure DevOps
Ниже приведена полная конфигурация конвейера Azure DevOps:
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'x64'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller@1
displayName: 'Install NuGet'
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
displayName: 'NuGet restore'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
displayName: 'Build solution'
- task: CopyFiles@2
inputs:
SourceFolder: '$(ProgramFiles(x86))\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\CodeCoverage\15.0.0\Tools'
Contents: 'Microsoft.VisualStudio.CodeCoverage.Shim.dll'
TargetFolder: '$(Build.SourcesDirectory)\tools'
displayName: 'Copy Shim DLL'
- script: |
regsvr32 /s "C:\Program Files (x86)\IIS Express\iisexpress.exe"
set CODE_COVERAGE_PATH=$(Build.SourcesDirectory)\tools
set PATH=$(CODE_COVERAGE_PATH);%PATH%
displayName: 'Register COM Components and Set Environment'
- task: VSTest@2
inputs:
testAssemblyVer2: |
**\IntegrationTests.dll
searchFolder: '$(Build.SourcesDirectory)'
vsTestVersion: 'toolsVersion'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
codeCoverageEnabled: true
runSettingsFile: 'codecoverage.runsettings'
testFiltercriteria: 'TestCategory!=Integration || TestName~*Fast*'
overrideTestrunParameters: |
-TestEnvironment="CI"
displayName: 'Run integration tests with coverage'
Пример GitHub Actions
name: CI Pipeline
on:
push:
branches: [ main ]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.1
- name: Install dependencies
run: nuget restore MySolution.sln
- name: Build solution
run: msbuild MySolution.sln -p:Configuration=Release
- name: Copy Shim DLL
run: |
mkdir -p tools
copy "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\CodeCoverage\15.0.0\Tools\Microsoft.VisualStudio.CodeCoverage.Shim.dll" tools/
- name: Register COM and Setup Environment
run: |
regsvr32 /s "C:\Program Files (x86)\IIS Express\iisexpress.exe"
set CODE_COVERAGE_PATH=%CD%\tools
set PATH=%CODE_COVERAGE_PATH%;%PATH%
- name: Run tests with coverage
run: vstest.console.exe IntegrationTests.dll /EnableCodeCoverage /logger:trx
env:
CODE_COVERAGE_PATH: %CD%\tools
Долгосрочный путь миграции
Рассмотрите переход на .NET Core/5+
Для новых проектов рассмотрите миграцию на .NET Core или .NET 5+, которые имеют лучшую поддержку CI/CD:
// Подход к интеграционным тестам .NET Core
[Fact]
public async Task WebApiIntegrationTest()
{
// Используйте TestServer вместо IIS Express
using var factory = new WebApplicationFactory<Startup>();
using var client = factory.CreateClient();
var response = await client.GetAsync("/api/values");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
Assert.Contains("Test", content);
}
Контейнеризация ваших тестов
Используйте Docker‑контейнеры для более стабильных тестовых окружений:
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore MySolution.sln
RUN dotnet build MySolution.sln --configuration Release
FROM build AS test
WORKDIR /src
COPY --from=build /src .
RUN dotnet test MySolution.sln --configuration Release --collect:"XPlat Code Coverage"
Используйте фреймворки для тестирования API
Рассмотрите замену IIS Express на фреймворки тестирования API, которые не требуют веб‑хостинга:
// Используйте TestServer для тестов, похожих на unit‑тесты
[Fact]
public async Task TestWebApiEndpoint()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<Startup>()
.UseUrls("http://localhost:5000");
using var server = new TestServer(webHostBuilder);
using var client = server.CreateClient();
var response = await client.GetAsync("/api/values");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
Внедрив эти решения, вы сможете успешно запускать интеграционные тесты IIS Express с покрытием кода в CI‑pipeline, минимизируя зависимость от COM‑компонентов и проблемных зависимостей.
Источники
- Stack Overflow – IIS Express integration tests failing in CI with coverage
- GitHub – Microsoft vstest Shim dependency issues
- GitHub – Microsoft vstest Environment variables and child processes
- JetBrains support – dotCover IIS support removal
- OpenCover GitHub repository
- NCover support – IIS testing considerations
- CodeMaze – .NET Code Coverage guide
Заключение
Чтобы решить проблемы с IIS Express интеграционными тестами в CI‑pipeline при включённом покрытии кода для приложений .NET Framework:
- Разверните Shim DLL вручную в CI‑окружение и установите правильные переменные среды, чтобы тестовый раннер мог её найти.
- Регистрация COM‑компонентов, необходимых IIS Express, либо рассмотрите альтернативные решения хостинга, такие как Kestrel или полноценный IIS.
- Используйте оптимизированные runsettings с
AllowLowIntegrityProcessesиCollectFromChildProcessesдля лучшей совместимости с CI. - Рассмотрите альтернативные инструменты покрытия, такие как OpenCover или Coverlet, которые работают лучше с IIS Express в CI‑окружениях.
- Планируйте миграцию на .NET Core/5+ с TestServer для более надёжных интеграционных тестов без зависимостей от IIS Express.
Самое быстрое решение – вручную развернуть Shim DLL и зарегистрировать COM‑компоненты, но для долгосрочной стабильности рассмотрите миграцию на .NET Core с использованием TestServer‑базированных интеграционных тестов, которые устраняют зависимости от IIS Express, сохраняя при этом полноценное покрытие кода.