С приходом .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 файл с конфигурацией сборки вы можете найти здесь.
Спасибо за внимание!