Нарушение сборки в TeamCity, если модульные тесты .NET Core, работающие под Docker, имеют покрытие кода менее 90% - PullRequest
0 голосов
/ 04 сентября 2018

Недавно я смотрел на Docker и на то, как я могу использовать TeamCity для запуска модульных тестов .NET Core в контейнерах Docker как части моего конвейера сборки. Я добавляю это в качестве последней строки в моем Dockerfile, чтобы иметь возможность запускать тесты:

ENTRYPOINT ["dotnet", "test", "--verbosity=normal"]

Затем на эти файлы Docker ссылаются в файлах compose, которые TeamCity создает и запускает с помощью docker-compose в командной строке.

У меня это успешно работает сейчас. Следующая задача - сломать сборку, если охват модульных / интеграционных тестов составляет менее 90% - или какое-либо другое значение - без аргументов по этому поводу, пожалуйста!

Я успешно использую зависимость NuGet coverlet.msbuild для измерения покрытия кода как части моей сборки. Это хорошо работает и в TeamCity, и я вижу вывод в моей сборке TeamCity.

Я получил эту работу, добавив файл coverlet.msbuild в каждый из моих тестовых проектов и изменив точку входа Dockerfile на:

ENTRYPOINT ["dotnet", "test", "--verbosity=normal", "/p:CollectCoverage=true", "/p:Threshold=90", "/p:ThresholdType=line"]

Вывод сборки TeamCity показывает таблицы ASCII с результатами, но пока я не смог найти хороший способ разбить сборку, если покрытие кода недостаточно высоко. Оставленный на своих собственных устройствах, TeamCity не помечает сборки как сбойные, если покрытие кода слишком низкое, что достаточно справедливо, так как не экстрасенсорно!

Я наивно думал, что смогу создать условие сбоя в TeamCity, которое обнаружит наличие следующего текста:

'[Assemnbly]' has a line coverage '9.8%' below specified threshold '95%'

... используя регулярное выражение вроде этого:

has a line coverage '((\d+(\.\d*)?)|(\.\d+))%' below specified threshold '((\d+(\.\d*)?)|(\.\d+))%'

Однако, когда проверяемые библиотеки DLL ссылаются на другие библиотеки DLL, которые тестируются отдельно, это становится непростым делом, поскольку coverlet.msbuild сообщает метрики покрытия для всех «затронутых» библиотек DLL. Например, у меня есть тестовый проект под названием Steve.Core.Files.Tests, который тестирует Steve.Core.Files. Однако Steve.Core.Files в свою очередь ссылается на Steve.Core.Extensions. Я тестирую Steve.Core.Extensions отдельно в своей собственной тестовой DLL, поэтому меня не волнуют результаты для этой DLL при тестировании файлов. Вывод в TeamCity выглядит следующим образом:

+-----------------------+--------+--------+--------+
| Module                | Line   | Branch | Method |
+-----------------------+--------+--------+--------+
| Steve.Core.Extensions | 23.5%  | 40%    | 40%    |
+-----------------------+--------+--------+--------+
| Steve.Core.Files      | 100%   | 100%   | 100%   |
+-----------------------+--------+--------+--------+

... поэтому происходит сбой на основе 23,5% бита, даже если рассматриваемая DLL равна 100%. На самом деле это очень затрудняет проверку использования условия сбоя Regex.

Чтобы усложнить ситуацию, я запускаю все тесты во всех сборках, используя один динамический Dockerfile, по двум причинам:

  1. Я не хочу менять файл Dockerfile и docker-compose (и TeamCity) каждый раз, когда добавляю больше проектов и тестов.

  2. Существует множество зависимостей между библиотеками DLL, поэтому имеет смысл собрать их один раз и протестировать все вместе.

Это означает, что я не хочу разделять тесты так, чтобы у каждого был свой Dockerfile - я знаю, что это позволило бы мне использовать флаги исключения / включения для получения желаемого поведения.

У кого-нибудь есть другие идеи, как я могу решить эту проблему, пожалуйста?

Я надеюсь, что смогу добавить файл на уровне каждого тестового проекта, чтобы сообщить ему, для каких библиотек следует охватить - это было бы лучшим решением. В противном случае, поскольку я использую строгое соглашение об именах между проектами и тестовыми проектами, могу ли я добавить переключатель в мою команду dotnet test, чтобы тестировать только сборку с тем же именем, что и у тестовой сборки, минус бит .Tests в конце?

Спасибо заранее; помощь оценена!

Приветствия

Стив.

Обновление от 7 сентября 2018 года:

Итак, мои файлы Docker теперь специфичны для каждого проекта модульного тестирования. Они выглядят так и существуют рядом с файлами тестового проекта:

FROM microsoft/dotnet:2-sdk

# Set the working directory:
WORKDIR /src

# Copy the solution file and the NuGet.config across to the src directory:
COPY *.sln NuGet.config ./

# Copy the main source project files to the root level:
COPY */*.csproj ./

# Make directories for each project file and move the project file to the correct place:
RUN for file in $(ls *.csproj); do mkdir -p ${file%.*}/ && mv $file ${file%.*}/; done

# Restore dependencies:
RUN dotnet restore

# Copy all files so that we have all everything ready to compile:
COPY . .

# Set the flag to tell TeamCity that these are unit tests:
ENV TEAMCITY_PROJECT_NAME = ${TEAMCITY_PROJECT_NAME}

# Run the tests:
ENTRYPOINT ["dotnet", "test", "Steve.Core.Configuration.Tests/Steve.Core.Configuration.Tests.csproj", "--verbosity=normal", "/p:CollectCoverage=true", "/p:Threshold=95", "/p:ThresholdType=line", "/p:Exclude=\"[Steve.Core.Testing]*\""]

Обратите внимание на параметр исключения, который должен останавливать результаты покрытия для библиотеки Steve.Core.Testing, включаемой в результаты для Steve.Core.Configuration, которая является основной зависимостью тестов, и проекта, который тестируется модулем. .

Мой составной файл выглядит следующим образом и существует рядом с файлом решения:

version: '3.6'

services:
  # Dependencies:
  steve.core.ldap.tests.ldap:
    image: osixia/openldap
    container_name: steve.core.ldap.tests.ldap
    environment:
      LDAP_ORGANISATION: Steve
      LDAP_DOMAIN: steve.com
      LDAP_ADMIN_PASSWORD: Password1
  steve.core.data.mysql.tests.database:
    image: mysql
    container_name: steve.core.data.mysql.tests.database
    command: mysqld --default-authentication-plugin=mysql_native_password
    environment:
      - MYSQL_ROOT_PASSWORD=Password1
      - MYSQL_DATABASE=testdb
  steve.core.data.sqlserver.tests.database:
    image: microsoft/mssql-server-linux
    container_name: steve.core.data.sqlserver.tests.database
    environment:
      - MSSQL_SA_PASSWORD=Password1
      - ACCEPT_EULA=Y
      - MSSQL_PID=Developer  
  steve.core.email.tests.smtp:
    image: mailhog/mailhog 
    container_name: steve.core.email.tests.smtp  

  # Steve.Core.Configuration:
  steve.core.configuration.tests:
    image: steve.core.configuration.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Configuration.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Data.MySql:
  steve.core.data.mysql.tests:
    image: steve.core.data.mysql.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Data.MySql.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Data.SqlServer:
  steve.core.data.sqlserver.tests:
    image: steve.core.data.sqlserver.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Data.SqlServer.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Data:
  steve.core.data.tests:
    image: steve.core.data.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Data.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Email:
  steve.core.email.tests:
    image: steve.core.email.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Email.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Encryption:
  steve.core.encryption.tests:
   image: steve.core.encryption.tests:tests
   build:
     context: .
     dockerfile: Steve.Core.Encryption.Tests/Dockerfile
   environment:
     - TEAMCITY_PROJECT_NAME

  # Steve.Core.Execution:
  steve.core.execution.tests:
    image: steve.core.execution.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Execution.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Extensions:
  steve.core.extensions.tests:
    image: steve.core.extensions.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Extensions.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Files:
  steve.core.files.tests:
    image: steve.core.files.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Files.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Ldap:
  steve.core.ldap.tests:
    image: steve.core.ldap.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Ldap.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Maths:
  steve.core.maths.tests:
    image: steve.core.maths.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Maths.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

  # Steve.Core.Time:
  steve.core.time.tests:
    image: steve.core.time.tests:tests
    build:
      context: .
      dockerfile: Steve.Core.Time.Tests/Dockerfile
    environment:
      - TEAMCITY_PROJECT_NAME

Когда он запускается в TeamCity, он сообщает только о 7 тестах из двух проектов (по какой-то странной причине), хотя в 12 проектах есть 236 тестов.

Я буду рад выслать результаты сборки TeamCity по электронной почте, если это поможет.

Кто-нибудь знает, как я могу снова запустить мои тесты, пожалуйста?

Спасибо

Стив.

1 Ответ

0 голосов
/ 10 сентября 2018

Таким образом, единственное решение состояло в том, чтобы разделить каждый проект модульного теста на его собственный файл составления, который включает зависимости, необходимые только для этой тестовой DLL. (например, mailhog для тестирования почтовых DLL, SQL Server для тестирования библиотек DLL и т. д.). TeamCity затем запускает их все по отдельности, используя один скрипт, подобный следующему:

docker-compose -f docker-compose-configuration-tests.yml up --force-recreate --abort-on-container-exit --build
docker-compose -f docker-compose-configuration-tests.yml down --volumes --remove-orphans

docker-compose -f docker-compose-data-mysql-tests.yml up --force-recreate --abort-on-container-exit --build
docker-compose -f docker-compose-data-mysql-tests.yml down --volumes --remove-orphans

...

Каждый из них имеет свой собственный Dockerfile, который создает тестовую DLL и устанавливает исключения DLL для покрытия модульных тестов. TeamCity выкладывает результаты всех тестов за один этап сборки, и условие сбоя покрытия кода регулярного выражения, упомянутое в моем вопросе выше, затем корректно обнаруживает тестовые проекты, которые не достигают покрытия x%, и нарушает сборку.

Теперь, чтобы понять, как интегрировать проверку кода (например, современные эквиваленты FxCop и StyleCop) в мой процесс сборки ...

...