در این مقاله مقدماتی قراره یک ماشین حساب در پایتون بسازیم، با استفاده از فریمورک Pytest براش تست خودکار یکپارچه بنویسیم، از یک Jenkins کانتینر شده برای Fetch کردن مخزن گیت‌هاب استفاده کنیم و آزمایش‌ها رو داخل یک کانتینر داکر ساخته شده توسط Jenkins اجرا کنیم. کل مخزن رو در این لینک می‌تونین ببینین.

پیش نیازها

.      یک ماشین با سیستم عامل ویندوز

2.      داکر دسکتاپ برای ویندوز که روی کانتینرهای لینوکس سوییچ شده

3.      پایتون 3.x

4.      PyCharm، یا هر IDE مناسب دیگه برای پایتون

5.      Git

6.      اکانت گیت‌هاب

پیشنهاد می‌کنیم، پست بلاگ خودکارسازی در ابر چیست؟ رو مطالعه کنید.

قسمت اول: پروژه Pytest ما

بیاین دایرکتوری پروژه رو با نام python-test-calculator ایجاد کنیم و با دستور git init به عنوان مخزن گیت‌هاب تعیینش کنیم. یک محیط مجازی داخل این پروژه درست کنید (در PyCahrm وقتی یک پروژه جدید ایجاد می‌کنید این فرایند به صورت خودکار اتفاق میفته). میشه پوشه venv رو با قرار دادنش در فایل .gitignore. ندید گرفت.  Pytest، که قراره فریمورک تست خودکار یکپارچه ما باشه رو با استفاده از دستور pip install pytest نصب کنین. فایل calculator.py که شامل تعدادی تابع ریاضی برای تست هست رو ایجاد کنین. این فایل به همراه فایل __init__.py داخل یک پوشه src قرار می‌گیره. (یک فایل خالی که به ما اجازه میده از این پوشه به عنوان پکیج استفاده کنیم).

<!-- wp:paragraph -->
<p>def add(a, b):</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	checkInputs(a, b)</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	return a + b</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>def subtract(a, b):</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	checkInputs(a, b)</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	return a - b</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>def multiply(a, b):</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	checkInputs(a, b)</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	return a * b</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>def divide(a, b):</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	checkInputs(a, b)</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	return a / b</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>def checkInputs(a, b):</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>	if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>    	raise TypeError("Inputs must be either int or float!")<br></p>
<!-- /wp:paragraph -->

ست‌ها داخل یک پوشه به اسم tests قرار دارن که شامل یک دسته فایل برای تست هر تابع ریاضی هستن. به طور پیش فرض pytest تنها فایل‌هایی رو به عنوان فایل تست شناسایی می‌کنه که نام آن‌ها با عبارت test_ شروع یا با عبارت _test خاتمه پیدا کنه. من ده تست ساده برای ماشین حسابمون نوشتم. برای مثال در فایل test_addition.py ما یک تابع جمع که در فایل calculator.py تعریف شده رو Import می‌کنیم، پارامترهای ورودی رو وارد کرده و با استفاده از دستور assert اونها رو برای بررسی خروجی مورد انتظار اعتبار‌سنجی می‌کنیم. فایل خالی __init__.py در پوشه tests به ما اجازه میده دستور pytest رو به صورت مستقیم در دایرکتوری root اجرا کنیم.

# test_addition.py
from src.calculator import add
import pytest
def test_add():
	result = add(3, 4)
	assert result == 7
def test_add_string():
	with pytest.raises(TypeError):
    	add("string", 4)

دستور pip freeze > requirements.txt برای ذخیره تمام پکیج‌های نصب شده داخل محیط مجازی اجرا کنید. این به شما کمک می‌کنه با اجرای دستور pip install -r requirements.txt متعلقات تابع رو داخل کانتینر تست نصب کنید.

فایل pytest.ini من فقط یک خط کد junit_family=xunit1 رو داره که به من اجازه میده خطاهای نمایش داده شده موقع استفاده از گزینه –junitxml به همراه اجرای دستور pytest رو ندیده بگیرم.

این پروژه رو داخل گیت‌هاب قرار بدین.

قسمت دوم: استفاده از DockerFile برای تست

پس کی قراره از داکر استفاده کنیم؟ 

یادتون باشه، ایده ما این بود که دو تا کانتینر داشته باشیم: یکی برای اجرای تست‌ها و دیگری برای اجرای Jenkins (بعدا این رو میبینیم). 

مطمئن بشین که روی کانتینرهای لینوکس سوییچ کردین، با اجرای دستور docker version باید مقدار OS/Arch زیر خط Server: Docker Engine – Community برابر با linux/amd64 باشه. بیاین یه فایل داکر ایجاد کنیم که شامل کد زیره:

FROM python:3.6-slim
MAINTAINER varunkumar032@gmail.com
COPY . /python-test-calculator
WORKDIR /python-test-calculator
RUN pip install --no-cache-dir -r requirements.txt
RUN ["pytest", "-v", "--junitxml=reports/result.xml"]
CMD tail -f /dev/null

اینجا ما داریم از فایل ایمیج پایتون برای اجرای تست‌ها استفاده می‌کنیم چون روی این فایل نسخه مورد نیاز پایتون به صورت پیش فرض نصب شده. 

دستور COPY کد رو از محیط کاری ما به یک دایرکتوری جدید python-test-calculator کپی و دستور WORKDIR این دایرکتوری رو به عنوان دایرکتوری اصلی در کانتینر معرفی می‌کنه. 

دستور RUN به شما اجازه اجرای دستورات نصب و تست رو میده.

 گزینه –v برای فعال کردن حالت واژگان و از فایل result.xml برای ذخیره گزارش تست خودکار یکپارچه استفاده میشه.

دستور CMD tail -f /dev/null کانتینر رو حتی بعد از پایان تست در حالت اجرا نگه میداره تا ما بتونیم فایل result.xml رو از کانتینر به فضای کارمون در jenkins کپی کنیم تا برای انتشار نتایج تست آماده بشه.

قسمت سوم: اجرای jenkins با استفاده از داکر

همون طور که همه ما می‌دونیم، Jenkins یک ابزار خودکارسازی منبع باز برای اهداف CI/CD (ادغام مداوم/پیاده سازی مداوم) است.  ما می‌تونیم با دانلود و اجرای فایل Jenkins با پسوند WAR اون رو نصب کنیم، اما پیش نیاز نصبش پشتیبانی دستگاه از جاواست. خب بیاین Jenkins رو که تمام متعلقاتش (dependencies) نصب شده داخل یک کانتینر اجرا کنیم، تا بتونه یک فایل ایمیج داکر بسازه. روش‌های دیگه‌ای هم برای انجام این کار هست که پیچیدگی‌های خاص خودشون رو دارن، اما بهترین راه استفاده از همین روشه. فایل  JenkinsDockerfile رو ایجاد کنید، که در واقع یه Dockerfile دیگه با اسم متفاوته تا با فایل قبلی اشتباه گرفته نشه، و شامل کد زیره:

FROM jenkins/jenkins:lts
USER root
RUN apt-get update -qq \
	&& apt-get install -qqy apt-transport-https ca-certificates \
	curl gnupg2 software-properties-common
RUN curl -fsSL https://download.docker.com/linux/debian/gpg \
	| apt-key add -
RUN add-apt-repository \
	"deb [arch=amd64] https://download.docker.com/linux/debian \
	$(lsb_release -cs) \
	stable"
RUN apt-get update -qq \
	&& apt-get install docker-ce=17.12.1~ce-0~debian -y

jenkins/jenkins:lts یه فایل ایمیجه که ما روی اون داکر رو نصب می‌کنیم. فایل ایمیج Jenkins که با داکر درست شده رو با استفاده از دستور زیر بسازین:

docker build -t jenkins-docker-image -f JenkinsDockerfile .

jenkins-docker-image نام ایمیج ماست. کانتینر رو در حالی که فرایند docker daemon سوار شده، اجرا کنید تا بتونیم با استفاده از دستور زیر، دستورات داکر رو داخل کانتینر Jenkins اجرا کنیم:

docker run -d -p 8080:8080 --name jenkins-docker-container -v /var/run/docker.sock:/var/run/docker.sock jenkins-docker

با استفاده از دستور docker ps –a مطمئن بشین jenkins-docker-container در حال اجراست.

مسیر http://localhost:8080/ رو در مرورگر خودتون باز کنید تا صفحه Unlock Jenkins رو ببینید. رمز عبور رو از داخل docker exec -it jenkins-docker-container cat var/jenkins_home/secrets/initialAdminPassword کپی و پلاگین‌های پیشنهاد شده رو نصب کنین. یک کاربر ادمین بسازین و جزئیات حساب کاربری رو کامل کنید و URL پیش فرض رو نگه دارین. روی دکمه Save and Finish کلیک و در مرحله بعد Start Using Jenkins رو بزنین. صفحه پیش فرض Jenkins مطابق شکل زیر باید براتون نمایش داده بشه:

روی create new jobs کلیک و یک پروژه freestyle ایجاد کرده و اسمش رو هر چیزی که دوست دارین بذارین. در برگه General می‌تونین توضیحات پروژه رو بنویسین و URL پروژه گیت‌هاب رو مشخص کنین.

در برگه Source Code Management روی Git کلیک و آدرس مخزن رو وارد کنین. در مسیر Add->Jenkins نام کاربری و رمز عبور اکانت گیت رو وارد و گواهی نامه‌های پروژه رو مشخص کنین. در این گام مخزن python-test-calculator از آدرس گیت‌هاب مشخص شده در مسیر /var/jenkins_home/workspace کپی میشه تا فضای کاری داخل دایرکتوری python-test-calculator ساخته بشه.

در برگه Build بر روی Add build step->Execute shell کلیک و کد زیر رو در این قسمت کپی کنید:

IMAGE_NAME="test-image"
CONTAINER_NAME="test-container"
echo "Check current working directory"
pwd
echo "Build docker image and run container"
docker build -t $IMAGE_NAME .
docker run -d --name $CONTAINER_NAME $IMAGE_NAME
echo "Copy result.xml into Jenkins container"
rm -rf reports; mkdir reports
docker cp $CONTAINER_NAME:/python-test-calculator/reports/result.xml reports/
echo "Cleanup"
docker stop $CONTAINER_NAME
docker rm $CONTAINER_NAME
docker rmi $IMAGE_NAME

در اسکریپت shell، ما به Jenkins دستور میدیم تا فایل ایمیج با عنوان test-image رو با استفاده از Dockerfile بسازه. در این گام دستور pytest ما همان طور که در Dockerfile مشخص شده، اجرا میشه. ما کانتینر رو از ایمیج به عنوان test-container اجرا می‌کنیم و در همون حالت اجرا نگه می‌داریم مگر در شرایطی که به خاطر استفاده از دستور tail -f /dev/null موقع شروع اجرا، برنامه متوقف بشه. سپس ما فایل result.xml را از داخل کانتینر به پوشه reports که تازه ساخته شده، منتقل می‌کنیم. دستور Cleanup فایل ایمیج و کانتینر رو متوقف و پاک می‌کنه.

در برگه Post-Build Actions ما محل فضای کاری فایل result.xml رو که کپی کردیم، برای انتشار مشخص می‌کنیم. مطمئن بشین که پلاگین result.xml در Jenkins نصب شده. تنظیمات پروژه را ذخیره کنین.

در پروژه خودتون بر روی Build Now کلیک کنید تا معجزه اتفاق بیفته:

تبریک می‌گیم!! شما همین الان اولین سیستم تست خودکار خودتون رو ساختین، که در اون شما یک برنامه رو با استفاده از Jenkins داخل یک کانتینر اختصاصی تست کردین تا یک کد از مخزن ریموت را Fetch و گزارش‌ها رو تولید کنین.

شما می‌تونین این فرایند رو طوری توسعه بدین که بتونین هر برنامه‌ای رو داخل یک محیط ایزوله به روش خودکار تست کنین. فقط کافیه تنظیمات درست رو انجام بدین.

خودکارسازی خوبی داشته باشین!!