دانستنی‌ها

ابزار‌های کاربردی Maven برای اجرای Integration Test ها

برای تست پروژه‌های جاوا EE ابتدا باید سرور روشن شود، سپس پروژه build شده و در سرور قرار داده می‌شود، در این مرحله می‌توان آزمون‌ها را اجرا کرد. پس از آن می‌توانیم سرور را خاموش کنیم. تمامی این کارها می توانند به کمک Maven به صورت اتوماتیک انجام شوند. در این مقاله به صورت مختصر ابزار‌های مربوطه را معرفی کرده و روش کار با آن‌ها را نشان می‌دهیم.

خواننده باید از قبل بداند:

  • فایل های اجرایی ساخته شده از پروژه های جاوا EE در داخل یک سرور مانند Tomcat یا jetty اجرا می‌شوند.
  • برای ساخت فایل‌های اجرایی از Build Tool هایی مانند Maven استفاده می‌شود.
  • Maven همچنین می‌تواند وابستگی‌ (Dependency) های پروژه به کتابخانه‌ها را مدیریت کند و پکیج این کتابخانه‌ها را دانلود و در محل مناسب، در داخل پروژه قرار دهد. همچنین خدماتی برای build کردن پروژه در اختیار قرار می‌دهد.

محاسن توسعه‌ی تست محور (TDD)

توسعه ی تست‌محور یا Test Driven Development که در آن باید همزمان با توسعه‌ی نرم‌افزار، تست‌هایی برای بررسی صحت عملکرد آن نوشت، باعث می‌شود اشکالات کدنویسی به سرعت کشف و اصلاح شوند، تغییرات بعدی هنگام بازآرای (Refactoring) را تسهیل کرده و بررسی ایده‌های جدید را ممکن می‌سازد. به این صورت که می‌توانیم یک قسمت از کد را تغییر دهیم و با اجرای موفق تست‌ها (پاس شدنشان) مطمئن شویم کاری که کرده‌ایم جای دیگری از کد را خراب نکرده است. این ها فقط چند نمونه از فواید بسیار زیاد توسعه تست‌محور است.

Unit – Integrated – Validation testing

امروزه تلاش می‌شود یک نرم‌افزار کامل به جزء های کوچک‌تر (چندین ماژول) تقسیم شود و تا حد ممکن این قسمت‌ها وابستگی کمتری به همدیگر داشته باشند. ماژول‌های کوچک‌تر، راحت‌تر کد‌نویسی می‌شوند، قابل فهم‌ترند و چون یک وظیفه دارند، تست کردن آنها راحت‌تر است و اگر نرم‌افزار درست کار نکند به سادگی می‌توان مشکل را پیدا و رفع کرد.

تلاش برای کد نویسی ماژولار باعث می‌شود در فرایند توسعه‌ی نرم‌افزار نیازمند مراحل مختلف تست باشیم.

اگر یک نرم افزار از سه ماژول B ، A و C تشکیل شده باشد، در مرحله ی آزمون واحد (Unit Testing) تست‌هایی می‌نویسیم که مشخص می‌کنند هر ماژول وظیفه‌ی خود را به درستی انجام می‌دهد یا نه. در مرحله ی Integration Testing تست‌هایی می‌نویسیم تا تعامل ماژول‌ها با هم (مثلا A با B و A با C) سنجیده شود. در مرحله ی Validation Testing باید عملکرد کلی نرم‌افزار کنترل شود و به دنبال پاسخ این سوال هستیم که آیا نرم‌افزار در شرایط مختلف وظیفه‌ی خود را به درستی انجام می‌دهد و به نتایج مورد انتظار می‌رسد یا نه؟

پلاگین‌های Maven

هدف این مقاله نشان دادن عملکرد چند ابزار در Maven است که در مرحله‌های Integration در پروژه‌های جاوا اینترپرایز می‌توان از آن‌ها استفاده کرد. اجازه بدهید ببینیم در چه سناریویی به این ابزار‌ها نیاز پیدا می‌کنیم.

می‌خواهیم پروژه‌ی فوق را آماده‌ی عرضه کنیم. باید Source Code و تست‌های هر ماژول را کامپایل کنیم، مرحله‌ی آزمون واحد را با اجرای تست‌های مربوط به هر کدام انجام دهیم و اگر مشکلی نبود هر ماژول را پکیج‌بندی کرده در معرض دید ماژول‌های دیگر قرار دهیم تا بتوانیم تست‌های مرحله‌ی Integration را اجرا کنیم. برای اجرای هر کدام از این تست‌ها ابزار‌هایی مانند Maven Surefire Plugin و Maven Failsafe Plugin وجود دارد که اولی اجرای تست‌ها در مرحله‌ی Unit Testing و دومی در مرحله‌ی Integration Testing را بر عهده دارد.

نکته‌ی کلیدی در مورد تست‌ها این است که برای اجرای آن‌ها نیازمند کار زیادی نباشیم تا هر موقع بخواهیم به سادگی بتوانیم «دوباره بررسی کنیم همه چیز در کد رو به راه هست یا نه؟». حالا تصور کنید کدی می‌نویسیم که روی سرور اجرا می‌شود. در این صورت کاری که باید در هر بار تست انجام دهیم این است که سرور را روشن کنیم. بعد پروژه را داخل سرور deploy کنیم. تست‌ها را اجرا کنیم و اگر همه‌ی تست ها پاس شدند، می‌توانیم در نهایت سرور را خاموش کنیم. آیا می‌توان این مرحله از کار را هم به صورت اتوماتیک توسط Maven انجام داد؟

بلی! برای این کار می توانیم از ابزار هایی مثل Jetty Maven Plugin یا Tomcat Maven Plugin استفاده کنیم. این ابزار‌ها می‌توانند کار روشن و خاموش کردن آن را برای اجرای تست ها، در روال Maven برای تولید اتوماتیک یک پکیج آماده‌ی deploy انجام دهند.

Maven Build Life Cycle

در طول مقاله از عبارت «روال اتوماتیک Maven برای رساندن کد به مرحله ی deploy شده در سرور» استفاده کردیم. بهتر است نگاهی دقیق‌تر به این این روال یا به زبان Mavenای‌ها Life Cycle بیندازیم. وقتی دستوری مانند mvn deploy صادر می‌کنیم، Maven پروژه‌ی ما را از چند فاز متوالی عبور می‌دهد که چند نمونه از آنها را مرور می‌کنیم:

شرح نام فاز
کامپایل سورس‌کد compile
کامپایل تست‌ها test-compile
اجرای آزمون‌های واحد test
کد‌های کامپایل‌شده را به پکیج‌های مناسب مانند jar یا war و … تبدیل می‌کند. package
مرحله‌ی پیش از اجرای تست‌های Integration

این مرحله زمان مناسبی برای روشن کردن سرور است

pre-integration-test
اجرای تست‌های Integration integration-test
مرحله‌ی پس از اجرای تست های Integration

این مرحله زمان مناسبی برای خاموش‌کردن سرور است

post-integration-test
قرار دادن پکیج تولیدشده در یک محل مناسب

(عموما مخزن محلی یا Local Repository)

install
قرار دادن package تولیدشده در یک محل مناسب

(عموما داخل سرور یا Remote Repository)

deploy

در هر کدام از این فاز ها که Maven پروژه را از آن ها عبور می‌دهد، می‌توانیم یک یا چند Plugin را به Maven معرفی کنیم تا Maven دستورات آن پلاگین خاص را روی پروژه انجام دهد. به عنوان مثال در فاز های pre-integration-test با کمک ‌Jetty Maven Plugin سرور را روشن خواهیم کرد، هم‌چینن در مرحله‌ی integration-test می‌توانیم به کمک plugin مربوطه تست‌ها را اجرا کنیم.

Talk is cheap, show me the code

تنظیمات مروبوط به Plugin‌های Maven در فایل pom.xml نوشته می‌شود.

به عنوان مثال maven-surefire-plugin ، در پروژه، به این صورت معرفی و تنظیم می‌شود:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>	<!-- 1 -->
	<artifactId>maven-surefire-plugin</artifactId>	<!-- 2 -->
	<version>2.20</version>				<!-- 3 -->
	<configuration>
	    <skip>true</skip>	<!-- 4 -->
	</configuration>
	<executions>			<!-- 5 -->
		<execution>
			<phase>integration-test</phase>		<!-- 6 -->
			<goals>
			    <goal>test</goal>			<!-- 7 -->
			</goals>
			<configuration>
			    <skip>false</skip>			<!-- 8 -->
			</configuration>
		</execution>
	</executions>
</plugin>

همان‌طور که معمولا یک فرد را با شمردن چند ویژگی مانند نام، نام خانوادگی و نام پدر به صورت دقیق مشخص کنیم، هر پکیج که Maven مورد استفاده قرار می‌دهد با یک سری خواص از بقیه متمایز و معرفی می‌شود. ۱ و ۲ و ۳ خاصیت‌هایی هستند که با آن‌ها maven-surefire-plugin را به پروژه‌ی خود معرفی کردیم تا maven آن را از مخازنش گرفته و مورد استفاده قرار دهد.

هر پلاگین می‌تواند یک سری اعمال را روی پروژه انجام دهد که این اعمال به اصطلاح Goalهای یک پلاگین نامیده می شوند.

بخش مهم دیگر در تکه کد فوق قسمت <execution> است. گفتیم هر پلاگین می تواند یک یا چند Goal داشته باشد. از سوی دیگر در هر فاز می‌توانیم Goalهای پلاگین‌های مختلف را اجرا کنیم. برای این که مشخص کنیم «در چه فازی، چه Goalای، از چه پلاگینی، با چه تنظیماتی اجرا شود» از executionها استفاده می‌کنیم.

به عنوان مثال در قسمت بالا می‌خواهیم بگوییم در فاز integration-test تست‌های پروژه را اجرا کن و تنظیمات این اجرا را در بخش configuration می‌آوریم که بسیار ساده است.

اما بخش 4 به چه معناست؟ همان طور که پیش‌تر اشاره شد surefire-plugin برای اجرای unit-testها ساخته شده است. اما ما با اعمال تنظیمات فوق آن را برای اجرای تست ها در فاز integration-test استفاده کردیم. در نتیجه باید در بخش 4 تنظیم می‌کردیم که در فاز معمول کاری این پلاگین که test است، از اجرای آزمون‌ها منصرف شود.

قطعه کد زیر تنظیمات jetty-maven-plugin را در فایل pom.xml نشان می‌دهد:

  <plugin>
  	<groupId>org.eclipse.jetty</groupId>
  	<artifactId>jetty-maven-plugin</artifactId>
  	<version>9.4.6.v20170531</version>
  	<configuration>								<!-- 1 -->
  		<webApp>							<!-- 2 -->
				<contextPath>/cjxrs</contextPath>
  		</webApp>
			<stopKey>foo</stopKey>					<!-- 3 -->
			<stopPort>9191</stopPort>				<!-- 4 -->
		  			
			<scanIntervalSeconds>5</scanIntervalSeconds>	<!-- 5 -->
			
  			<httpConnector>						<!-- 6 -->
  				<port>9090</port>
  				<idleTimeout>60</idleTimeout>
  			</httpConnector>
  	</configuration>
  	<executions>								<!-- 7 -->
	        <execution>							<!-- 8 -->
	            <id>start-jetty</id>
	            <phase>pre-integration-test</phase>
	            <goals>
	                <goal>start</goal>
	            </goals>
	            <configuration>
	                <scanIntervalSeconds>0</scanIntervalSeconds>
	                <daemon>true</daemon>
	            </configuration>
	        </execution>
	        <execution>							<!-- 9 -->
	            <id>stop-jetty</id>
	            <phase>post-integration-test</phase>
	            <goals>
	                <goal>stop</goal>
	            </goals>
	        </execution>
	</executions>
</plugin>

در این قسمت هم اول با سه شناسه jetty-maven-plugin را معرفی می‌کنیم. سپس در بخش 2 تنظیم می‌کنیم که URI پروژه بعد از Host Name و Port Number چه باشد. در بخش 3 به سروری که پلاگین آن را راه‌اندازی می‌کند می‌گوییم که در پورت 9191 منتظر پیام foo باش و هر موقع این پیام رسید سرور را خاموش کن. با استفاده از بخش 6 یک سرور instantiate می شود. تنظیمات بیشتری در این قسمت می توان اعمال کرد که از آن صرف نظر کرده و به تنظیمات پیش فرض اکتفا می‌کنیم. فقط پورت سرور برای ارتباط روی پروتکل http و زمان خاموش شدنِ سرور در صورت بلااستفاده بودن را مشخص می‌کنیم.

دو کار مهم توسط این plugin باید انجام شود که عبارتند از روشن کردن و خاموش کردن سرور jetty در فازهای پیش از integration-test و پس از آن. برای هر کدام از این ها یک execution تعریف می کنیم (۸ و ۹) و هر دو execution را در داخل یک تگ executions می‌گنجانیم.

پروژه با دستور mvn install و یا در eclipse IDE از قسمت زیر قابل build کردن است: راست کلیک روی نام پروژه، Run‌As و سپس Maven‌Install

که خروجی زیر را تولید می‌کند:

 

[INFO] Started o.e.j.m.p.JettyWebAppContext@379ce046{/cjxrs,file:///home/ahmad/Documents/workspace-eclipse/cjxrs/src/main/webapp/,AVAILABLE}{file:///home/ahmad/Documents/workspace-eclipse/cjxrs/src/main/webapp/}
[INFO] Started ServerConnector@4779aae6{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
[INFO] Started @5993ms
[INFO] Started Jetty Server
[INFO] سرور راه اندازی شد
[INFO] --- maven-surefire-plugin:2.20:test (default) @ cjxrs ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.rhotiz.jaxrs.service.PersonServiceTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.02 s - in com.rhotiz.jaxrs.service.PersonServiceTest
[INFO] Running com.rhotiz.jaxrs.domain.PersonTest
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in com.rhotiz.jaxrs.domain.PersonTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
[INFO] تست ها اجرا و پاس شدند
[INFO] 
[INFO] --- jetty-maven-plugin:9.4.6.v20170531:stop (stop-jetty) @ cjxrs ---
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ cjxrs ---
[INFO] Stopped ServerConnector@4779aae6{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
[INFO] Stopped scavenging سرور خاموش شد
[INFO] Installing /home/ahmad/Documents/workspace-eclipse/cjxrs/target/cjxrs-0.0.1-SNAPSHOT.war to /home/ahmad/.m2/repository/com/rhotiz/jaxrs/cjxrs/0.0.1-SNAPSHOT/cjxrs-0.0.1-SNAPSHOT.war
[INFO] Installing /home/ahmad/Documents/workspace-eclipse/cjxrs/pom.xml to /home/ahmad/.m2/repository/com/rhotiz/jaxrs/cjxrs/0.0.1-SNAPSHOT/cjxrs-0.0.1-SNAPSHOT.pom
[INFO] -------------فایل های ساخته شده در محل مناسب قرار گرفتند
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

در تصویر فوق مراحل مربوط به راه اندازی jetty، اجرای تست‌ها، خاموش‌کردن سرور و پیام مربوط به Build Success مشخص است.

فایل پروژه از لینک زیر قابل دسترسی است:

https://github.com/AhmadHoghooghi/SimpleJAX-RS

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا