مشخصات مقاله
آزمودن واحدهای برنامه با jUnit
آموزش حاضر به شرح unit testing (اجرای تست نرم افزاری بر روی بخش های مختلف پروژه به صورت مجزا) با استفاده از ابزار JUnit 4.x می پردازد. این آموزش نحوه ی ایجاد تست های jUnit را شرح داده و سپس استفاده از محیط برنامه نویسی Eclipse را در راستای نوشتن تست های نرم افزاری لازم جهت کسب اطمینان از عملکرد صحیح بخش های مختلف اپلیکیشن را توضیح می دهد.
هدف از نوشتن تست های نرم افزاری چیست؟
شرح مفهوم تست های نرم افزاری
تست اپلیکیشن (software test) یک قطعه ی نرم افزاری (کد) هست که خود یک قطعه ی نرم افزاری دیگر را (جهت آزمایش و اطمینان از کارکرد صحیح و اینکه آیا قطعه ی نوشته شده هدف مورد نظر را برآورده می سازد) اجرا می کند. این تست نرم افزاری بررسی می کند آیا قطعه کد نوشته شده منجر به وضعیت مورد انتظار می شود (که به آن state testing گویند) و یا مراحل لازم (سلسه رخداد ها) به ترتیب رخ می دهند یا خیر (که به آن behavior testing گویند).
چرا استفاده از تست ها توصیه می شود؟
Unit test به توسعه دهنده کمک می کند تا منطق یک تکه از برنامه را آزمایش کرده و بدین وسیله مطمئن شود رفتار مورد انتظار را از خود نشان می دهد/هدف و نیاز مدنظر را برآورده می سازد یا خیر.
در واقع با نوشتن و اجرای unit test بر روی بخش های نرم افزاری به صورت مجزا این امکان فراهم می شود تا کاستی ها و خطاهایی که ممکن است با اضافه شدن رفتار / اعمال تغییرات جدید به برنامه در آن رخنه کرده باشند، توسط توسعه دهنده به سرعت کشف شده و برطرف گردد. نوشتن تست برای بخش های متعددی از اپلیکیشن به برنامه نویس کمک می کند تا بعده ها بدون اینکه مجبور به نوشتن تست های دستی (manual test) شود، بتواند قابلیت های نوین زیادی با عملکرد صحیح به اپلیکیشن خود اضافه نماید.
فریم ورک های تست گیری (testing frameworks) برای Java
فریم ورک های تست گیری متعددی برای Java نوشته شده و در دسترس می باشد. از جمله ی پرکاربرد و محبوب ترین آن ها می توان به JUnit و TestNG اشاره کرد.
مقاله ی حاضر بر روی نحوه ی استفاده از ابزار JUnit متمرکز می شود.
واژه ها و مفاهیم مرتبط با تست گیری
کد (یا اپلیکیشن) مورد تست /Code Under Test
کدی که تست بر روی آن اجرا می شود در اصطلاح Code Under Test (کد مورد آزمایش) خوانده می شود. حال اگر واحد مورد تست شما یک آپلیکیشن باشد، در آن صورت application under test اطلاق خواهد شد.
Test fixture (مقادیر ثابت تست)
Test fixture عبارت است از state ثابت (اطلاعات مربوط به وضعیت) چند آبجکت که به عنوان مقادیر پایه (baseline) برای اجرای تست بر روی بخش های مجزا نرم افزار مورد استفاده قرار می گیرد. مقصود اصلی استفاده از test fixture این است که محیط ثابت و مشخصی برای تست وجود داشته باشد به طوری که نتایج / خروجی حاصل تکرارپذیر باشد. می توان از آن به عنوان پیش شرط های تست نیز نام برد. به عبارت دیگر، هرچه که بایستی از قبل آماده باشد تا یک تست اجرا شده و نتیجه ی مورد نظر را به عنوان خروجی انتظار داشت، text fixture خوانده می شود.
به عنوان مثال، text fixture می تواند یک رشته ی ثابت باشد که به عنوان پارامتر ورودی ثابت به یک متد پاس داده می شود. در نهایت تست مشخص می کند آیا متد مورد آزمایش نتایج مورد انتظار را تولید می کند / عملکرد صحیح را ارائه می دهد یا خیر.
شرح مفهوم Unit test و تست بخش های مختلف نرم افزار (کد) به صورت مجزا
Unit test یک قطعه کد است که توسعه دهنده برای تست قابلیتی (کسب اطمینان از عملکرد صحیح کد) از برنامه آن را نوشته و انتظار دارد که نتیجه ی (رفتار یا وضعیت) دلخواه را تولید کند.
آن درصدی از کل کد که مورد آزمایش قرار می گیرد، در اصطلاح test coverage اطلاق می گردد.
Unit test معمولا بخش یا قطعات (مستقل) کوچکی از برنامه را در سطح کلاس یا متد مورد آزمایش قرار می دهند. لازم به ذکر است که dependency ها و کتابخانه های خارجی را می بایست از unit test ها حذف کرد. این کار را می توان با جایگزین کردن این کتابخانه ها با پیاده سازی و بدنه ی تست یا آبجکت ساختگی که از پیش توسط framework ساخته شده، انجام داد.
گفتنی است که Unit test ها کارایی چندانی برای تست رابط های کاربری پیچیده و تعامل داده بین کامپوننت های نرم افزاری ندارند. برای این منظور بهتر است integration test طراحی نمایید.
Integration test (تست اپلیکیشن در context و بستر خودش)
Integration test عبارت است از آزمایش اپلیکیشن در context و بستر کلی خودش به طوری که برای تست نیازمند بخش های دیگر اپلیکیشن باشد (به عبارت دیگر آن قطعه هایی از کد که بر روی بخش های دیگر تاثیر جانبی/side effect دارند). Integration test بر روی رابطه و بخش های مشترک چند کامپوننت نرم افزاری تمرکز می کند. این نوع تست همچنین اطمینان حاصل می کنند که کل سیستم به درستی و مورد انتظار رفتار می کند و در این حین نیاز برای نوشتن و اجرای تست های دقیق دستی را کاهش می دهند.
این نوع تست ها به شما امکان می دهند تا پیشنهادات و بازخورد کاربران از اپلیکیشن را در مجموعه تست هایی (test suite) که برای آزمایش و کسب اطمینان از عملکرد صحیح اپلیکیشن نوشته اید، لحاظ نمایید. از این جهت، خروجی تست مربوطه مشابه تعامل مورد انتظار بین کاربر و اپلیکیشن خواهد بود.
Performance test (تست های بررسی کارایی)
Performance test یا تست های ارزیابی کارایی به طور مکرر برای بررسی میزان کارایی و مستند سازی قدرت کامپوننت های نرم افزاری مورد استفاده قرار می گیرند. تست ذکر شده را توسعه دهنده برای این می نویسد که میزان کارایی کد مورد آزمایش را در شرایطی که بار کاری سنگین هست بررسی نموده و از بالا بودن بازده در هر شرایطی اطمینان حاصل کند.
Behavior testing و State testing
تستی که بررسی می کند آیا متدهایی با پارامترهای ورودی مورد نظر فراخوانی شده اند یا خیر، (behavior test) interaction test خوانده می شوند. Behavior test نتیجه ی متد فراخوانی شده را اعتبار سنجی نمی کند.
State test (تست وضعیت) ، بر خلاف behavior test، در بررسی و آزمایش رفتار اپلیکیشن مورد تست خلاصه می شود.
چنانچه الگوریتم یا قابلیت های سیستمی را مورد آزمایش قرار داده اید، در آن صورت تست شما بایستی بر روی state متمرکز شود، نه تعامل بین کامپوننت ها.
در این تست ها غالبا از آبجکت های ساختگی (mock) و stub (کد جایگزین) کلاس های مرتبط برای استخراج، حذف تعامل یا وابستگی با دیگر کلاس ها استفاده می شود. پس از آن شما می توانید (اطلاعات مربوط به) وضعیت یا رفتار را بر اساس نیاز خود تست نمایید.
سازماندهی تست
تست در کجا بایستی نوشته شود
Unit test های معمولی اغلب در پروژه ی مجزا یا پوشه ی اصلی (source folder) جداگانه جایگذاری می شوند. این کار سبب می شود کد تست شده از کد اصلی جدا نگه داشته شود.
کدام بخش نرم افزار بایستی تست شود
اینکه دقیقا می بایست چه بخش هایی تست شود، یک موضوع بسیار بحث برانگیز است. برخی از توسعه دهندگان بر این عقیده هستند که تمامی دستورات داخل کد می بایست مورد بررسی قرار گرفته و تست شود.
به هر حال شما بایستی برای بخش های پیچیده و حساس نرم افزار خود تست بنویسید. اگر ویژگی و قابلیت های جدید به اپلیکیشن خود اضافه نمایید، آنگاه یک مجموعه تست قدرتمند و کارامد می تواند اپلیکیشن شما را در برابر ضعف و خطاهایی که ممکن است بعدها به داخل اپلیکیشن و کد جاری راه پیدا کنند محافظت خواهد کرد.
در طراحی تست نرم افزاری می توان بخش های جزئی از کد را نادیده گرفت. برای مثال، نوشتن تست برای متدهای getter و setter که صرفا مقادیری را به فیلدهایی تخصیص می دهند، تقریبا بیهوده است. در واقع نوشتن تست برای تمامی دستورات داخل کد مشخصا بسیار زمان بر بوده و تقریبا امری بی فایده می باشد چرا که شما اپلیکیشن را در بستر JVM تست می کنید. JVM به خودی خود test case هایی از پیش تعریف شده و مشخص دارد.
در صورت طراحی تست برای code base هایی که قبلا تستی برای آن ها نوشته نشده، بهتر است ابتدا برای آن بخش هایی از کد تست تهیه طراحی نمایید که دفعات رخداد خطا در گذشته در آن ها زیاد باشد. سپس می توانید بر روی بخش های اساسی کد (اپلیکیشن) متمرکز شوید.
استفاده از JUnit
JUnit framework (فریم ورک انجام unit test بر روی پروژه های جاوا)
4.x JUnit در واقع یک فریم ورک اجرای unit test بر روی پروژه های جاوایی است که با استفاده از annotation ها، متدهایی که رفتارها و قابلیت های تست را به صورت جداگانه آزمایش می کنند با علائم یا دستورهای خاص نشانه گذاری و معرفی می نماید. برای کسب اطلاعات بیشتر در زمینه ی این فریم ورک تست گیری می توانید به آدرس http://junit.org/ و https://github.com/junit-team/junit مراجعه نمایید.
نحوه ی طراحی یک تست در فریم ورک تست گیری JUnit؟
JUnit test در حقیقت یک متد است که داخل کلاس تعریف شده و منحصرا برای منظور تست مورد استفاده قرار می گیرد. کلاسی که میزبان متد مذکور می باشد در اصطلاح test class نامیده می شود. به منظور نوشتن یک تست بر مبنای JUnit 4، متد مورد نظر را با دستور @org.junit.Test داخل کلاس میزبان نشانه گذاری (annotate) می کنید.
این متد کد مورد آزمایش را اجرا می کند. می توانید از متد assert که خود JUnit ارائه می دهد استفاده نمایید و یا از متدی که فریم ورک assert دیگری فراهم می نماید، بهره بگیرید. متد assert در اصل برای بررسی نتیجه ی واقعی با نتایج مورد انتظار مورد استفاده قرار می گیرد. متدهایی که به این صورت استفاده می شوند در اصطلاح تست گیری، assert ها یا دستورات assert خوانده می شوند.
لازم است در دستورات assert پیغام های معنی دار قرار دهید چرا که در صورت مواجه شدن با خطا کاربر سریع تر می تواند مشکل را شناسایی کرده و برطرف نماید. این ویژگی می تواند به ویژه برای فردی که کد را می خواند اما در نوشتن آن شرکت نداشته، مفید باشد.
نمونه ای از JUnit test
کد زیر یک نمونه از پیاده سازی تست JUnit را نمایش می دهد. این تست فرض را بر این می گذارد که کلاس MyClass وجود داشته و متدی به نامmultiply(int, init) را دربردارد.
1 2 3 4 5 6 7 8 9 10 11 12 13 | import static org.junit.Assert.assertEquals; import org.junit.Test; public class MyTests { @Test public void multiplicationOfZeroIntegersShouldReturnZero() { MyClass tester = new MyClass(); // MyClass is tested // assert statements assertEquals( "10 x 0 must be 0" , 0 , tester.multiply( 10 , 0 )); assertEquals( "0 x 10 must be 0" , 0 , tester.multiply( 0 , 10 )); assertEquals( "0 x 0 must be 0" , 0 , tester.multiply( 0 , 0 )); } } <button></button> |
قرارداد/روش های نام گذاری تست های مبتنی بر JUnit
قراردادهای نام گذاری متعددی برای تخصیص اسم به تست های طراحی شده بر اساس فریم ورک JUnit وجود دارد. یک راه حل پر طرفدار افزودن پسوند "-test" به اسم کلاس های تست گیری (test case) و جایگذاری آن ها در پکیج جدید "test" می باشد.
به طور کلی، اسم تست باید درباره ی کاربرد و مورد استفاده ی آن توضیح دهد.در صورتی که اسم تخصیص داده شده به تست، مورد کاربرد آن را روشن بیان نماید، دیگر نیازی به خواندن پیاده سازی و محتوای کد نخواهد بود.
یک قرارداد ممکن و پرطرفدار استفاده از کلمه ی "should" در اسم متد تست گیری می باشد. برای مثال، می توان به "ordersShouldBeCreated" یا "menuShouldGetActive" اشاره کرد. با این نوع انتخاب اسم برای متد، مشخص می شود که متد مورد نظر دقیقا چه رفتاری را تست کرده و انتظار چه نتیجه ای را دارد.
قراردادهای نام گذاری JUnit برای Maven
در صورت استفاده از سیستم کامپایل (build system) Maven، بهتر است از "Test" بجای "Tests" به عنوان پسوند استفاده نمایید. سیستم کامپایل Maven (به واسطه ی افزونه ی surefire خود) به صورت اتوماتیک و درون ساخته این کلاس ها را داخل test scope خود دربردارد.
مجموعه تست های JUnit (JUnit test suites)
در صورتی که چندین کلاس تست گیری برای اپلیکیشن نوشته باشید، بد نیست تمامی آن ها را در یک قالب واحد به نام test suite یا مجموعه تست بگنجانید. شایان ذکر است که اجرای test suite به مثابه ی اجرای تمامی کلاس های تست گیری، به ترتیب از قبل مشخص شده است که در مجموعه تست تعریف شده اند.
Test suite یک مجموعه ی بزرگ از تست ها است که خود می تواند test suite های دیگر را دربرگیرد.
نمونه کد زیر نحوه ی استفاده از test suite را نمایش می دهد. test suite حاضر دو کلاس تست گیری MyClassTest و MySecondClassTest را شامل می شود. در صورت تمایل می توانید به این کلاس های تست گیری، کلاس دیگری اضافه نمایید. کافی است کلاس مورد نظر را به دستور @Suite.SuiteClasses الحاق کنید.
1 2 3 4 5 6 7 8 9 10 11 | package com.vogella.junit.first; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith (Suite. class ) @SuiteClasses ({ MyClassTest. class , MySecondClassTest. class }) public class AllTests { } <button></button> |
اجرای تست از طریق خط فرمان/command line
می توانید تست مورد نظر را خارج از محیط برنامه نویسی (IDE) و به وسیله ی کدهای استاندارد جاوا راه اندازی کنید. برنامه نویس قادر خواهد بود با ترکیب سیستم های کامپایلی (build system) نظیر Apache Maven یا Gradle با یک Continuous Integration Server (همچون Jenkins) تست ها را به صورت خودکار و در فواصل زمانی منظم بر روی بخش هایی از نرم افزار به اجرا در آورد.
کلاس org.junit.runner.JunitCore متدی به نام runClasses() را ارائه می دهد. این متد به توسعه دهنده امکان می دهد تا همزمان یک یا چند کلاس تست گیری (test class) را اجرا کند. کلاس مزبور در خروجی آبجکتی از جنس org.junit.runner.Result برمی گرداند. این آبجکت را می توان جهت بازیابی اطلاعات مربوط به تست ها مورد استفاده قرار داد.
کلاس زیر نمایش می دهد چگونه می توان MyClassTest را اجرا نمود. این کلاس، کلاس تست گیری (test class) شما را اجرا کرده و خطاهای ممکن را در console درج می نماید.
1 2 3 4 5 6 7 8 9 10 11 12 13 | package de.vogella.junit.first; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class MyTestRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(MyClassTest. class ); for (Failure failure : result.getFailures()) { System.out.println(failure.toString()); } } } <button></button> |
کلاس حاضر را می توان مانند سایر برنامه های java در خط فرمان اجرا نمود. کافی است فایل JAR کتابخانه ی JUnit را به classpath اضافه نمایید.
ساختارهای پایه ای فریم ورک JUnit
Annotation های JUnit
JUnit 4.x با استفاده از annotation های خود، متدهای تست گیری را علامت گذاری کرده و امکان تنظیم اختصاصی آن ها را برای توسعه دهنده فراهم می آورد. جدول زیر لیستی از annotation های مهم در فریم ورک JUnit را به نمایش می گذارد.
public void ()method
public void ()method
public void ()method
public static void ()method
public static void ()method
دستورات Assert
JUnit کلاسی به نام Assert`class را شامل می شود. این کلاس توابع static ای را ارائه می دهد که شرایط خاصی را چک می کنند. دستورات assert معمولا با `assert آغاز می شوند. متد assertion، خروجی واقعی تست را با مقدار مورد انتظار مقایسه می کند و چنانچه مقایسه با شکست مواجه شد، یک AssertionException صادر می کند.
جدول زیر این توابع را به صورت اجمالی همراه با شرح عملکرد هریک به نمایش می گذارد. پارامترهای ذکر شده در []، اختیاری بوده و از جنس String هستند.
ترتیب اجرای تست
فریم ورک اجرای تست JUnit فرض را بر این می گذارد که تمامی متدهای آزمایشی (test method) با ترتیبی غیر مشخص و دلخواه اجرا شوند. در واقع تستی که به صورت کارامد نوشته شده باشد، نباید ترتیب اجرای خاصی داشته باشد. تست هایی که برای آزمایش بخش های مختلف برنامه نوشته می شود، نباید به سایر تست ها وابستگی داشته باشد.
از JUnit 4.11 به بعد، به صورت پیش فرض توصیه می شود از ترتیب deterministic و نه predictable برای اجرای تست بر روی نرم افزار استفاده نمایید.
می توانید با استفاده از یک annotation مشخص کنید که متدهای تست گیری (test method) بر اساس اسم متد مرتب شوند (در واقع بر اساس ترتیب حروف الفبا). به منظور فعال سازی این ویژگی، بایستی کلاس تست گیری (test class) خود را با @FixMethodOrder(MethodSorters.NAME_ASCENDING) علامت گذاری (annotate) نمایید. همچنین می توانید با استفاده از پارامتر MethodSorters.DEFAULT در annotation، به صورت صریح، حالت پیش فرض را تنظیم نمایید. بعلاوه می توانید از MethodSorters.JVM که از تنظیمات پیش فرض JVM استفاده می کند، بهره بگیرید که ممکن است از یک اجرا به اجرای دیگر متفاوت باشد.
غیرفعال سازی تست ها
@Ignore این امکان را فراهم می آورد که یک تست را به صورت static غیرفعال کرده و در اصطلاح آن را در تست نادیده بگیرید. یا می توانید با استفاده از پارامترهای Assume.assumeFalse یا Assume.assumeTrue یک شرط برای تست تعریف نمایید. در صورتی که شرط نتیجه ی true بدهد و برقرار باشد، پارامتر Assume.assumeFalse تست مربوطه را نامعتبر (invalid) معرفی و نشانه گذاری می کند. چنانچه شرط نتیجه ی false بدهد، پارامتر Assume.assumeTrue پس از ارزیابی تست، آن را نامعتبر معرفی و علامت گذاری می کند. به طور مثال، دستور زیر یک تست را در سیستم عامل Linux را غیرفعال می کند:
1 2 | Assume.assumeFalse(System.getProperty( "os.name" ).contains( "Linux" )); <button></button> |
پشتیبانی محیط برنامه نویسی Eclipse از JUnit
ایجاد تست های JUnit
در صورت تمایل می توانید تست های JUnit را خود به صورت دستی بنویسید، اما محیط کاری Eclipse این امکان را برای شما فراهم می آورد تا تست های JUnit را به صورت ویزادی و طبق یک روال از پیش تعیین شده تهیه نمایید.
برای مثال، می توان از ایجاد تست JUnit یا یک کلاس تست گیری جهت آزمایش عملکرد کلاس از پیش نوشته شده نام برد. برای این منظور بر روی کلاس جدید راست کلیک کرده، کلاس مورد نظر را از Package Explorer_ view انتخاب نمایید. سپس بار دیگر بر روی آن راست کلیک کرده و مسیر New ▸ JUnit Test Case را طی کنید.
در صورت تمایل می توانید این کار را به صورت ویزاردی و با استفاده از JUnits Wizards انجام دهید. برای دستیابی به این راهنمای نصب ویزاردی، کافی است مسیر رو به رو را دنبال کنید: File ▸ New ▸ Other… ▸ Java ▸ JUnit.
اجرای تست های JUnit
محیط کاری Eclipse به توسعه دهنده این امکان را می دهد تا تست ها را به صورت تعاملی اجرا کند.
به منظور اجرای یک تست، کلاس تست گیری (test class) را انتخاب نموده، سپس بر روی آن راست کلیک کنید. حال گزینه ی Run-as ▸ JUnit Test را انتخاب کنید.
این کار JUnit را راه اندازی کرده و تمامی متدهای تست گیری (test method) را داخل کلاس اجرا می کند.
Eclipse به برنامه نویس اجازه می دهد تا با فشردن کلیدهای Alt+Shift+X به صورت همزمان، تست مورد نظر را در کلاس انتخابی اجرا کند.
برای اینکه فقط و فقط تست انتخابی اجرا شود، اشاره گر موس را بر روی اسم متد تست گیری قرار داده و سپس کلیدهای فوق را فشار دهید.
به منظور مشاهده ی نتیجه ی تست JUnit، محیط کاری Eclipse یک ابزار به نام JUnit view را ارائه می دهد. می توانید unit test های دلخواه خود را به صورت انفرادی در view مذکور انتخاب نموده و پس از راست کلیک بر روی آن و انتخاب گزینه ی Run، آن ها را مجددا اجرا نمایید.

به صورت پیش فرض، این view تمامی تست های موجود را به نمایش می گذارد. می توانید این view را طوری تنظیم نمایید که تنها تست های ناموفق (failed) را نمایش دهد.

همچنین می توانید view را طوری تنظیم کنید که تنها در صورت برخورد با یک تست ناموفق فعال شود.

محیط برنامه نویسی Eclipse تنظیماتی مرتبط با اجرای تست ها (run configurations) ایجاد می کند. جهت مشاهده و ویرایش آن ها می توانید به این صورت اقدام نمایید: Run ▸ Run Configurations… menu.
استخراج و بازیابی تست های ناموفق و stacktrace ها
جهت بازیابی لیست مربوط به تست ناموفق، بر روی نتیجه ی تست راست کلیک کرده و Copy Failure List را انتخاب نمایید. این کار سبب می شود تست های ناموفق و stack trace ها در clipboard کپی و درج شوند (stack trace عبارت است فرایند ردیابی اجرای کدهای برنامه در بخشی از حافظه به نام stack).

امکان static import در فریم ورک JUnit
Static import یک امکان است که به فیلدهایی که داخل کلاس به صورت public static تعریف شده اند، این اجازه را می دهد تا بدون ذکر اسم کلاس میزبان، بکار برده شوند.
دستورات assert فریم ورک مذکور، برای اینکه قابلیت نوشتن دستورات تست گیری (test statement) مختصر و بهینه را برای توسعه دهنده فراهم کنند، معمولا به صورت public static تعریف می شوند. تکه کد زیر یک دستور assert را با دو حالت ممکن (با امکان static imports و یکی بدون static import) به نمایش می گذارد.
1 2 3 4 5 6 7 8 | // without static imports you have to write the following statement Assert.assertEquals( "10 x 5 must be 50" , 50 , tester.multiply( 10 , 5 )); // alternatively define assertEquals as static import import static org.junit.Assert.assertEquals; // more code // use assertEquals directly because of the static import assertEquals( "10 x 5 must be 50" , 50 , tester.multiply( 10 , 5 )); <button></button> |
برنامه ی راهنماWizard/ برای ساختن مجموعه تست (test suite)
می توانید در محیط کاری Eclipse و با امکاناتی که در اختیار شما قرار می دهد، به راحتی یک test suite (مجموعه تست) ایجاد نمایید. برای نیل به این هدف، ابتدا کلاس های آزمایشی و تست گیری (test class) که می بایست در مجموعه (suite) گنجانده شود را از طریق کادر Package Explorer گزینش نموده، در این کادر بر روی آن ها راست کلیک نمایید و سپس مسیر رو به رو را طی کنید: New ▸ Other… ▸ JUnit ▸ JUnit Test Suite.

تست exception و خطاها
@Test (expected = Exception.class) دارای قابلیتی محدودی است چرا که تنها قادر است یک exception (خطا) را تست کند. به منظور تست کلی exception ها، می توانید از الگوی تست گیری (test pattern) زیر استفاده نمایید.
1 2 3 4 5 6 7 8 | try { mustThrowException(); fail(); } catch (Exception e) { // expected // could also check for message of exception, etc. } <button></button> |
تست نویسی برای افزونه ها (JUnit Plug-in Test)
توسعه دهنده می تواند با استفاده از تست های JUnit Plug-in برای افزونه های نرم افزاری خود نیز تست طراحی کند. این تست ها در بستر test runner ویژه راه اندازی شده که به یک نمونه ی مستقل VM برای اجرا در محیط Eclipse احتیاج دارد. در واقع متدهای تست گیری محصور در کلاس (test class)، داخل آن نمونه مجزا (instance) اجرا می شوند.
نصب JUnit
استفاده از JUnit با سیستم کامپایل Gradle
به منظور استفاده از JUnit در فایل Gradle build، لازم است کتابخانه (dependency) testCompile را به فایل build خود اضافه نمایید.
1 2 3 4 5 | apply plugin: 'java' dependencies { testCompile 'junit:junit:4.12' } <button></button> |
استفاده از JUnit با سیستم کامپایل Maven
به منظور استفاده از JUnit در فایل Maven build، می بایست کتابخانه (dependency) زیر را به فایل pom خود اضافه نمایید.
1 2 3 4 5 6 | < dependency > < groupid >junit</ groupid > < artifactid >junit</ artifactid > < version >4.12</ version > </ dependency > < button ></ button > |
استفاده از JUnit درون ساخته ی محیط کاری Eclipse
محیط برنامه نویسی Eclipse با یک ورژن خاص از JUnit در اختیار برنامه نویسان قرار می گیرد. اگر از Eclipse برای ساخت و توسعه ی اپلیکیشن خود بهره می گیرید، در آن صورت لزومی ندارد افزونه یا محتوای جدیدی از اینترنت دانلود نمایید.
دانلود کتابخانه ی JUnit
اگر می خواهید کتابخانه ی JUnit مورد استفاده را به صورت صریح کنترل کنید، در آن صورت کافی است فایل JUnit4.x.jar را از وب سایت زیر دانلود کنید. محتوای دانلود شده، junit-4.*.jar را دربردارد که همان کتابخانه ی JUnit است. این کتابخانه را به پروژه ی Java خود و classpath مورد نظر اضافه نمایید.
www.junit.org
تنظیم محیط برنامه نویسی Eclipse برای استفاده از امکان static import کتابخانه ی JUnits
محیط کاری Eclipse به خودی خود همیشه قابلیت ایجاد دستورات static import مربوطه را ندارد. شما می توانید این محیط برنامه نویسی را طوری تنظیم نمایید که با استفاده از امکان code completion خود توابع معمول و پرکاربرد JUnit را وارد متن برنامه کرده و دستورات static import را به صورت خودکار اضافه نماید. برای دستیابی به این هدف، ابتدا پنجره ی Preferences را از طریق Window ▸ Preferences باز نمایید و سپس مسیر رو به رو را طی کنید: Java ▸ Editor ▸ Content Assist ▸ Favorites. با استفاده از دکمه ی New Type دستورات زیر را به آن اضافه نمایید:
- org.junit.Assert
- org.hamcrest.CoreMatchers
- org.hamcrest.Matchers
با این کار متدهای assertTrue، assertFalse و assertEquals را مستقیما در Content Assists قابل دسترسی و آماده ی استفاده می نمایید.

اکنون شما می توانید با استفاده از Content Assists (که با فشردن کلیدهای Ctrl+Space به طور همزمان به سرعت در اختیار شما قرار می گیرد) به راحتی متد و import را اضافه نمایید.
تمرین: استفاده از JUnit
آماده سازی پروژه
یک پروژه ی جدید به نام com.vogella.junit.first ایجاد نمایید. سپس یک پوشه ی جدید به نام test در پروژه ی مزبور ایجاد کنید. برای این منظور بر روی پروژه ی مورد نظر راست کلیک کرده، Properties را انتخاب نمایید و این مسیر را طی کنید: Java ▸ Build Path. حال بر روی تب Source کلیک نموده و آن را انتخاب کنید.

دکمه ی Add Folder را کلیک کنید. پس از آن، دکمه ی Create New Folder را فشار دهید. اسم پوشه را test انتخاب نمایید.
نتیجه در تصویر زیر به نمایش گذاشته شده است.

در صورت تمایل می توانید با راست کلیک بر روی پروژه و انتخاب New ▸ Source Folder، یک پوشه ی اصلی جدید نیز اضافه نمایید.
ساخت یک کلاس Java
در پوشه ی src، پکیج com.vogella.junit.first را ایجاد کرده و سپس کلاس زیر را به آن اضافه نمایید.
1 2 3 4 5 6 7 8 9 10 11 | package com.vogella.junit.first; public class MyClass { public int multiply( int x, int y) { // the following is just an example if (x > 999 ) { throw new IllegalArgumentException( "X should be less than 1000" ); } return x / y; } } <button></button> |
ساخت و طراحی یک تست JUnit
بر روی کلاس جدید، داخل کادر Package Explorer، راست کلیک نموده و سپس این مسیر را طی نمایید: New ▸ JUnit Test Case.
در برنامه ی راهنمای نصب (ویزارد) که در زیر مشاهده می کنید، حتما دکمه ی رادیویی New JUnit 4 test را فعال کرده و سپس پوشه ی source را بر روی test تنظیم نمایید تا کلاس تست در این پوشه ایجاد شود.

بر روی دکمه ی Next کلیک کرده و متدهایی که می خواهید تست شوند را انتخاب نمایید.

اگر کتابخانه ی JUnit در classpath پروژه ی جاری قید نشده بود، در آن صورت محیط کاری Eclipse از شما می خواهد که حتما آن را اضافه نمایید. با این کار شما در واقع JUnit و امکانات آن را به پروژه ی خود اضافه می نمایید.

حال تست مورد نظر را با کد و محتوای زیر ایجاد نمایید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.vogella.junit.first; import static org.junit.Assert.assertEquals; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class MyClassTest { @Test (expected = IllegalArgumentException. class ) public void testExceptionIsThrown() { MyClass tester = new MyClass(); tester.multiply( 1000 , 5 ); } @Test public void testMultiply() { MyClass tester = new MyClass(); assertEquals( "10 x 5 must be 50" , 50 , tester.multiply( 10 , 5 )); } } <button></button> |
اجرای تست در محیط برنامه نویسی Eclipse
بر روی کلاس تست گیری (test class) جدید راست کلیک کرده و سپس گزینه ی Run-As ▸ JUnit Test را انتخاب نمایید.

نتیجه ی این تست ها در کادر (view) JUnit به نمایش گذاشته می شود. در مثال حاضر، طبق انتظار یک تست بایستی موفقیت آمیز باشد و اما دیگری در خروجی یک خطا صادر کند. خطای نام برده با نوار قرمز نمایش داده می شود.

تست حاضر به این خاطر ناموفق است که کلاس (ماشین حساب) در انجام عملیات مورد انتظار (ضرب) با مشکل مواجه می شود. در واقع به جای انجام عملیات ضرب، دو عدد را بر هم تقسیم می کند. حال اشکال فنی برنامه را برطرف نموده و بار دیگر تست را اجرا کنید تا نوار سبز در خروجی تست به نمایش گذاشته شود.
تنظیمات و امکانات پیشرفته ی JUnit
اجرای تست های دارای پارامتر مشخص (Parameterized test)
JUnit این امکان را برای شما فراهم می آورد تا پارامترهای دلخواه را در کلاس های تست گیری مورد استفاده قرار دهید. کلاس مورد نظر می تواند شامل یک متد تست گیری بوده و این متد هر بار با پارامترهای ارائه شده ی مختلف اجرا شود.
بایستی کلاسی که قرار است پارامترهای مختلف به آن ارسال شوند را با دستور @RunWith(Parameterized.class) علامت گذاری نمایید.
چنین کلاسی (کلاس تست گیری) باید یک متد static در خود داشته که با دستور @Parameters نشانه گذاری شده باشد. این متد یک مجموعه آرایه (array collection) تولید کرده و در خروجی بازمی گرداند. تک تک آیتم ها در این آرایه به عنوان پارامتر ورودی به متد تست گیری (test method) حاضر در این کلاس ارسال می شوند.
می توانید فیلدهای public را با دستور @Parameter نشانه گذاری نمایید و بدین سیله مقادیر تستی (مقادیر یا پارامترهای آزمایشی) را داخل تست تزریق نمایید
کد زیر نمونه ای از یک تست که پارامترهایی از بیرون به آن ترزیق می شوند را به نمایش می گذارد. تست حاضر متد multiply() از کلاس MyClass را که یک کلاس دورنی و nested داخل کلاس میزبان می باشد را با ارسال پارامتر به آن، به منظور تست، آزمایش می نماید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package testing; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; import java.util.Collection; import static org.junit.Assert.assertEquals; import static org.junit.runners.Parameterized.*; @RunWith (Parameterized. class ) public class ParameterizedTestFields { // fields used together with @Parameter must be public @Parameter public int m1; @Parameter (value = 1 ) public int m2; // creates the test data @Parameters public static Collection<object[]> data() { Object[][] data = new Object[][] { { 1 , 2 }, { 5 , 3 }, { 121 , 4 } }; return Arrays.asList(data); } @Test public void testMultiplyException() { MyClass tester = new MyClass(); assertEquals( "Result" , m1 * m2, tester.multiply(m1, m2)); } // class to be tested class MyClass { public int multiply( int i, int j) { return i *j; } } } <button></button> |
یا می توانید به جای استفاده از دستور @Parameter، تابع سازنده (constructor) را بکار ببرید. در این تابع مقادیر مورد استفاده در هر تست نگهداری می شود. لازم به ذکر است که تعداد المان های موجود در آرایه که متد مورد نظر (متدی که با دستور فوق نشانه گذاری شده باشد) ارائه می دهد، بایستی با تعداد پارامترهای حاضر در تابع سازنده ی کلاس برابر باشد. در واقع به ازای هر پارامتر یک کلاس مستقل ایجاد شده و سپس مقادیر آزمایشی به واسطه ی تابع سازنده (constructor) به کلاس مورد نظر فرستاده می شوند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package de.vogella.junit.first; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith (Parameterized. class ) public class ParameterizedTestUsingConstructor { private int m1; private int m2; public ParameterizedTestUsingConstructor( int p1, int p2) { m1 = p1; m2 = p2; } // creates the test data @Parameters public static Collection<object[]> data() { Object[][] data = new Object[][] { { 1 , 2 }, { 5 , 3 }, { 121 , 4 } }; return Arrays.asList(data); } @Test public void testMultiplyException() { MyClass tester = new MyClass(); assertEquals( "Result" , m1 * m2, tester.multiply(m1, m2)); } // class to be tested class MyClass { public int multiply( int i, int j) { return i *j; } } } <button></button> |
حال اگر این کلاس تست گیری (test class) را اجرا کنید، متد حاضر در آن هر بار با پارامتر تعریف شده به صورت جداگانه اجرا می شود. به عنوان مثال، در نمونه ی فوق متد تست گیری مورد نظر سه بار اجرا می شود.
با مراجعه به آدرس اینترنتی https://github.com/Pragmatists/JUnitParams می توانید با روشی که نوشتن آن به مراتب آسان و معطف تر هست، آشنا شوید.
دستور (annotation) @Rule
Rule های کتابخانه ی JUnit به شما این امکان را می دهند تا رفتار و قابلیت های مورد نظر را به تک تک تست های داخل کلاس تست گیری (test class) اختصاص داده و اعمال کنید. می توانید فیلدهایی که از جنس TestRule هستند را با @Rule نشانه گذاری نمایید. همچنین می توانید آبجکت هایی ایجاد کنید که قابلیت تنظیم و استفاده از آن ها در متدهای تست گیری وجود داشته باشد. با این کار به میزان انعطاف پذیری تست شما افزوده می شود. به طور مثال، می توانید مشخص کنید که به هنگام اجرای کد آزمایشی، انتظار دارید که کدام پیغام خطا (exception message) نمایش داده شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package de.vogella.junit.first; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class RuleExceptionTesterExample { @Rule public ExpectedException exception = ExpectedException.none(); @Test public void throwsIllegalArgumentExceptionIfIconIsNull() { exception.expect(IllegalArgumentException. class ); exception.expectMessage( "Negative value not allowed" ); ClassToBeTested t = new ClassToBeTested(); t.methodToBeTest(- 1 ); } } <button></button> |
JUnit از قبل تعداد زیادی از rule ها را پیاده سازی نموده و آن ها را به صورت آماده در اختیار توسعه دهنده قرار می دهند. به طور مثال، کلاس TemporaryFolder این امکان را به شما می دهد تا فایل ها و پوشه هایی تنظیم کنید که خود به خود پس از هر بار اجرای تست حذف می شوند. نمونه ی زیر استفاده از پیاده سازی TemporaryFolder را به نمایش می گذارد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package de.vogella.junit.first; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class RuleTester { @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void testUsingTempFolder() throws IOException { File createdFolder = folder.newFolder( "newfolder" ); File createdFile = folder.newFile( "myfilefile.txt" ); assertTrue(createdFile.exists()); } } <button></button> |
طراحی و تنظیم rule های اختصاصی برای JUnit
جهت نوشتن rule های اختصاصی و دلخواه خود، شما می بایست اینترفیس TestRule را پیاده سازی نمایید. این اینترفیس، متد apply(Statement, Description) را در اختیار توسعه دهنده قرار می دهد که خروجی آن نمونه ای از Statement می باشد. پارامتر Statement نشانگر تست های موجود در بستر runtime JUnit است و Statement#evaluate() نیز تابعی است که این تست ها را به هنگام runtime، اجرا می کند. پارامتر Description، توصیف گر تست های فردی بوده و به شما اجازه می دهد تا از طریق reflection اطلاعات مرتبط با تست ها را بخوانید.
نمونه ی زیر یک مثال ساده از افزودن دستور log به اپلیکیشن اندرویدی، قبل و بعد از اجرای تست می باشد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package testing.android.vogella.com.asynctask; import android.util.Log; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; public class MyCustomRule implements TestRule { private Statement base; private Description description; @Override public Statement apply(Statement base, Description description) { this .base = base; this .description = description; return new MyStatement(base); } public class MyStatement extends Statement { private final Statement base; public MyStatement(Statement base) { this .base = base; } @Override public void evaluate() throws Throwable { System. Log.w( "MyCustomRule" ,description.getMethodName() + "Started" ); try { base.evaluate(); } finally { Log.w( "MyCustomRule" ,description.getMethodName() + "Finished" ); } } } } <button></button> |
به منظور استفاده از این rule، کافی است یک فیلد که با @Rule نشانه گذاری شده باشد به کلاس تست گیری (test class) خود اضافه نمایید.
1 2 3 | @Rule public MyCustomRule myRule = new MyCustomRule(); <button></button> |
Category ها و دسته بندی تست ها
می توان تست ها را دسته بندی نموده و یا آن ها را بر اساس اسم annotation ها، داخل دسته ی مورد نظر گنجاند یا از دسته ای حذف کرد. مثال زیر بر اساس نکات و رهنمودهای منتشر شده ی ویرایش JUnit 4.8 می باشد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public interface FastTests { /* category marker */ } public interface SlowTests { /* category marker */ } public class A { @Test public void a() { fail(); } @Category (SlowTests. class ) @Test public void b() { } } @Category ({ SlowTests. class , FastTests. class }) public class B { @Test public void c() { } } @RunWith (Categories. class ) @IncludeCategory (SlowTests. class ) @SuiteClasses ({ A. class , B. class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b and B.c, but not A.a } @RunWith (Categories. class ) @IncludeCategory (SlowTests. class ) @ExcludeCategory (FastTests. class ) @SuiteClasses ({ A. class , B. class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b, but not A.a or B.c } <button></button> |
ایجاد آبجکت های ساختگی یا شبیه سازی رفتار آبجکت/Mocking
برای اجرای تست بر روی بخش های مختلف پروژه ی نرم افزاری که همان unit testing نیز خوانده می شود، گاه لازم است از object mocking استفاده نمایید. در این سناریو آبجکت واقعی با یک آبجکت ساختگی که رفتار و عملکرد آبجکت مورد نظر را شبیه سازی می کند، در طول اجرای تست جایگزین می شود.
فریم ورک های متعددی برای mocking (جهت تست نرم افزار) وجود دارد. به منظور کسب اطلاعات بیشتر در مورد فریم ورک های شبیه سازی و تست نرم افزار می توانید به آموزش Mockito تحت آدرس http://www.vogella.com/tutorials/Mockito/article.html یا EasyMock تحت آدرس http://www.vogella.com/tutorials/EasyMock/article.html مراجعه فرمایید.