С приходом .Net Core у нас появилась прекрасная возможность не только запускать наш код на разных ОС, но и тестировать его на разных ОС. А что может быть лучше Docker при работе с разными ОС?

Тестирование более ценно, когда у вас нет никакой разницы между тестовой средой с целевыми средами. Представьте, что вы поддерживаете свое приложение на нескольких операционных системах или версиях операционной системы. С помощью Docker вы можете протестировать свое приложение в каждой из них.
В этой статье мы рассмотрим, как создать отдельный образ, в котором будут запускаться юнит тесты вашего приложения и настроить для всего этого CI/CD пайплайн в VSTS который Azure DevOps с недавних пор.
Если вы работаете с Docker, вероятно, вы используете многоэтапные (multi-stage builds) сборки для создания своих контейнеров. В таком случае вы объединяете создание бинарников (используя build образ) и создание финального образа (используя runtime образ) в рамках одного и того же Docker-файла.
Если ваша система состоит из одного контейнера, в этом случае наиболее обычным подходом может быть запуск тестов как часть процесса построения финального образа. То есть, запуск тестов в Dockerfile.
Чтобы сделать это в многоэтапном процессе, при запуске

Пока не будем беспокоиться о том, что делает веб-приложение. С другой стороны, у нас есть единственный тест, который проверяет поведение
Теперь создадим Dockerfile, который будет создавать образ WebApplication и в то же время запускать тесты:
Этот Dockerfile нужно поместить в каталог с солюшн файлом (СiCd.sln). Для создания образа используем команду:
Наш тест терпит неудачу (ошибка в
Теперь давайте посмотрим, как запустить этот процесс в Azure DevOps.
Наш build definition на данный момент представляет собой одну задачу типа «Docker»:

В итоге запуска, билд фейлится, потому что у нас падает тест. Кроме того, у нас нет результатов выполнения тестов (вкладка «Тест» пуста), так как не выполняется тестирование в понимании VSTS:

Запускать тесты как часть сборки образа не то чтобы совсем плохо, но это будет препятствовать тому, чтобы VSTS узнал о результате выполнение. Это связано с «ограничением» Docker, который не позволяет создавать тома во время
Мы будем придерживаться другого подхода и воспользуемся отличной альтернативой
Параметр
Здесь мы запускаем образ, созданный на предыдущем шаге, и через том сопоставляем каталог "tests" контейнера с каталогом хоста (в моем случае D:\CiCD\tests). В итоге, у меня в D:\CiCD\tests появились результаты тестов.
Для того чтобы построить финальный образ запускаем:
Преимущество заключается в том, что благодаря модели уровней Docker, нет необходимости повторно выполнять все остальные этапы (то есть нет необходимости перекомпилировать приложение).
Ну, давайте теперь применим все это к Azure DevOps pipelines. Чтобы упростить сборку и избежать большого количества параметров, мы будем использовать docker-compose. Наш docker-compose.yml имеет следующий контент:
Здесь мы определяем два образа (webapplication и webapplication-tests). Чтобы все было по канону, давайте добавим файл docker-compose.override.yml:
Отлично, теперь для запуска тестов нам просто нужно:
Эта команда запускает тесты и создает выходной trx-файл в каталоге, указанном переменной среды
Теперь можно отредактировать наш CI процесс в Azure DevOps. Для этого определим следующие шаги:
Начнем с первого шага, который представляет собой таск(задачу) Docker Compose в Azure:

Ставим
Дальше запускаем контейнер с юнит тестами:

Здесь надо выбрать
Третий шаг — «Опубликовать результаты тестов»:

Важно указать
Последний шаг будет пушать образы в хранилище. Для этого нужно указать Azure подписку, а также Azure Container Registry. Все готово, для того чтобы создать новый билд. Сохраняем. Запускаем. Если тесты не проходят, билд завершится неудачно, но теперь мы видим результаты в VSTS:

Надеюсь, данный материал был полезным. Мой yml файл с конфигурацией сборки вы можете найти здесь.
Спасибо за внимание!

Тестирование более ценно, когда у вас нет никакой разницы между тестовой средой с целевыми средами. Представьте, что вы поддерживаете свое приложение на нескольких операционных системах или версиях операционной системы. С помощью Docker вы можете протестировать свое приложение в каждой из них.
В этой статье мы рассмотрим, как создать отдельный образ, в котором будут запускаться юнит тесты вашего приложения и настроить для всего этого CI/CD пайплайн в VSTS который Azure DevOps с недавних пор.
Если вы работаете с Docker, вероятно, вы используете многоэтапные (multi-stage builds) сборки для создания своих контейнеров. В таком случае вы объединяете создание бинарников (используя build образ) и создание финального образа (используя runtime образ) в рамках одного и того же Docker-файла.
Если ваша система состоит из одного контейнера, в этом случае наиболее обычным подходом может быть запуск тестов как часть процесса построения финального образа. То есть, запуск тестов в Dockerfile.
Чтобы сделать это в многоэтапном процессе, при запуске
docker build вы выполняете тесты как еще один шаг в построении финального образа. Давайте посмотрим на простой пример. Допустим у нас есть два проекта: веб-приложения и юнит тесты:
Пока не будем беспокоиться о том, что делает веб-приложение. С другой стороны, у нас есть единственный тест, который проверяет поведение
GuidProvider и выглядит следующим образом:[Fact]
public void Never_return_a_empty_guid()
{
// Arrange & Act
var provider = new GuidProvider();
var id = provider.Id;
// Assert
Assert.NotEqual(Guid.Empty, id);
}Теперь создадим Dockerfile, который будет создавать образ WebApplication и в то же время запускать тесты:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY CiCd.sln .
COPY WebApplication/WebApplication.csproj WebApplication/
COPY WebApplication.Test/WebApplication.Test.csproj WebApplication.Test/
RUN dotnet restore
COPY . .
WORKDIR /src/WebApplication
RUN dotnet build --no-restore -c Release -o /app
FROM build as test
WORKDIR /src/WebApplication.Test
RUN dotnet test
FROM build AS publish
WORKDIR /src/WebApplication
RUN dotnet publish --no-build -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WebApplication.dll"]
Этот Dockerfile нужно поместить в каталог с солюшн файлом (СiCd.sln). Для создания образа используем команду:
docker build -t webapplication .Наш тест терпит неудачу (ошибка в
GuidProvider которая всегда возвращает Guid.Empty), поэтому сборка образа завершится неудачно:output
Step 15/22 : RUN dotnet test
---> Running in 423c27696356
Build started, please wait...
Build completed.
Test run for /src/WebApplication.Test/bin/Debug/netcoreapp2.1/WebApplication.Test.dll(.NETCoreApp,Version=v2.1)
Microsoft (R) Test Execution Command Line Tool Version 15.9.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
[xUnit.net 00:00:00.96] WebApplication.Test.GuidProviderTests.Never_return_a_empty_guid [FAIL]
Failed WebApplication.Test.GuidProviderTests.Never_return_a_empty_guid
Error Message:
Assert.NotEqual() Failure
Expected: Not 00000000-0000-0000-0000-000000000000
Actual: 00000000-0000-0000-0000-000000000000
Stack Trace:
at WebApplication.Test.GuidProviderTests.Never_return_a_empty_guid() in /src/WebApplication.Test/GuidProviderTests.cs:line 17
Test Run Failed.
Total tests: 1. Passed: 0. Failed: 1. Skipped: 0.
Test execution time: 2.8166 Seconds
The command '/bin/sh -c dotnet test' returned a non-zero code: 1Теперь давайте посмотрим, как запустить этот процесс в Azure DevOps.
Наш build definition на данный момент представляет собой одну задачу типа «Docker»:

В итоге запуска, билд фейлится, потому что у нас падает тест. Кроме того, у нас нет результатов выполнения тестов (вкладка «Тест» пуста), так как не выполняется тестирование в понимании VSTS:

Запускать тесты как часть сборки образа не то чтобы совсем плохо, но это будет препятствовать тому, чтобы VSTS узнал о результате выполнение. Это связано с «ограничением» Docker, который не позволяет создавать тома во время
docker build, поэтому мы не можем предоставить файл c результатами тестов (который можно сгенерировать с помощью dotnet test), этот файл остается в промежуточном контейнере, и мы не можем легко достать его оттуда.Мы будем придерживаться другого подхода и воспользуемся отличной альтернативой
docker run. Сначала поднимем отдельный контейнер и будем запустить тесты в нем. Для обоих контейнеров мы сможем использовать один и тот же Dockerfile. Для начала прежде всего нужно удалить строку, которая запускает dotnet test из Dockerfile, поскольку теперь мы будем запускать их отдельно. Хорошо, теперь давайте воспользуемся командой docker run, которая позволяет запускать Dockerfile до определенного этапа. В нашем случае это — этап тестирования:docker build -t webapplication-tests . --target testПараметр
-target указывает какой этап нужно собрать. Обратите внимание, что сгенерированный образ будет иметь название "webapplication-tests". Теперь можно запустить наши тесты и при этом сохранять файл "test-results.trx" с результатами их выполнения в каталоге "tests" контейнера:docker run -v/c/tests:/tests webapplication-tests --entrypoint "dotnet test --logger trx;LogFileName=/tests/test-results.trx"Здесь мы запускаем образ, созданный на предыдущем шаге, и через том сопоставляем каталог "tests" контейнера с каталогом хоста (в моем случае D:\CiCD\tests). В итоге, у меня в D:\CiCD\tests появились результаты тестов.
Для того чтобы построить финальный образ запускаем:
docker build -t webapplication . Преимущество заключается в том, что благодаря модели уровней Docker, нет необходимости повторно выполнять все остальные этапы (то есть нет необходимости перекомпилировать приложение).
Ну, давайте теперь применим все это к Azure DevOps pipelines. Чтобы упростить сборку и избежать большого количества параметров, мы будем использовать docker-compose. Наш docker-compose.yml имеет следующий контент:
version: '3.5'
services:
webapplication:
image: webapplication
build:
context: .
dockerfile: Dockerfile
webapplication-tests:
image: webapplication-tests
build:
context: .
dockerfile: Dockerfile
target: test
Здесь мы определяем два образа (webapplication и webapplication-tests). Чтобы все было по канону, давайте добавим файл docker-compose.override.yml:
version: '3.5'
services:
webapplication:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "8080:80"
webapplication-tests:
entrypoint:
- dotnet
- test
- --logger
- trx;LogFileName=/tests/test-results.trx
volumes:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests/}:/tests
Отлично, теперь для запуска тестов нам просто нужно:
docker-compose run webapplication-testsЭта команда запускает тесты и создает выходной trx-файл в каталоге, указанном переменной среды
BUILD_ARTIFACTSTAGINGDIRECTORY или используется дефолтное значение ./tests. Финальный образ делаем так:docker-compose build webapplicationТеперь можно отредактировать наш CI процесс в Azure DevOps. Для этого определим следующие шаги:
- Собрать все образы [build]
- Запустить юнит тесты [run]
- Опубликовать результат выполнения тестов [publish]
- Запушать образы в хранилище (Registry) [push]
Начнем с первого шага, который представляет собой таск(задачу) Docker Compose в Azure:

Ставим
Action: Build service images и указываем путь к docker-compose.yml. Дальше запускаем контейнер с юнит тестами:

Здесь надо выбрать
Action: Run a specific service image и указать имя контейнера Service Name: webapplication-tests. Также, не забываем о пути к docker-compose.yml и docker-compose.override.yml. Значение для Run in Background должно быть не установлено, в противном случае контейнер будет запускаться в «Detached mode» и задача не будет ожидать результатов выполнения тестов а перейдет к следующему шагу. Задача «Publish Test Results» будет пытаться опубликовать результаты, которых может еще не быть, так как запуск тестов занимает определенное время.Третий шаг — «Опубликовать результаты тестов»:

Важно указать
Run this task: Even if a previous task has failed, unless the build was canceled. Этот параметр важен, поскольку в противном случае результаты никогда не будут публиковаться, если тесты не прошли. Search folder: $(Build.ArtifactStagingDirectory) Последний шаг будет пушать образы в хранилище. Для этого нужно указать Azure подписку, а также Azure Container Registry. Все готово, для того чтобы создать новый билд. Сохраняем. Запускаем. Если тесты не проходят, билд завершится неудачно, но теперь мы видим результаты в VSTS:

Надеюсь, данный материал был полезным. Мой yml файл с конфигурацией сборки вы можете найти здесь.
Спасибо за внимание!