Недавно я смотрел на 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, по двум причинам:
Я не хочу менять файл Dockerfile и docker-compose (и TeamCity) каждый раз, когда добавляю больше проектов и тестов.
Существует множество зависимостей между библиотеками 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 по электронной почте, если это поможет.
Кто-нибудь знает, как я могу снова запустить мои тесты, пожалуйста?
Спасибо
Стив.