مشخصات مقاله
-
2008
-
0.0
-
5106
-
0
-
0
اجرای unit test بر روی اپلیکیشن با استفاده از فریم ورک تست گیری Mockito
تست بخش های مجزای نرم افزار با استفاده از آبجکت های ساختگی (mock objects)
هدف از نوشتن unit test و چالش هایی که در تست با آن ها مواجه می شوید
Unit test، همان طور که از اسم آن پیدا است، بایستی یک اپلیکیشن را در سطح کلاس های مجزا آزمایش نماید، به بیان دیگر هر یک از کلاس های تشکیل دهنده ی پروژه را به صورت جداگانه مورد تست قرار داده و از عملکرد صحیح تمامی آن ها اطمینان حاصل کند. برای نیل به این هدف در unit test، لازم است side effect (عوارض) یا وابستگی به سایر کلاس ها و بخش های سیستم حذف شود. بدیهی است که برای حذف این وابستگی ها، می بایست dependency به دیگر کلاس ها را حذف نمایید.
این کار با استفاده از جایگزین برای dependency های واقعی پروژه امکان پذیر می باشد.
طبقه بندی کلاس های تست گیری مختلف
یک dummy object/آبجکت خام بین بخش های مختلف پروژه پاس داده می شود اما هیچگاه توابع داخل آن به معنای واقعی فراخوانی نمی شوند (dummy object: آبجکتی که API و توابع کتابخانه ای یک آبجکت را عینا کپی نماید. در واقع این آبجکت ها به توسعه دهنده اجازه می دهند تا آبجکت هایی که کلاس های پروژه به آن نیاز دارند را، بدون اینکه پیاده سازی و رفتار خاصی برای آن تعریف کرده باشند، شبیه سازی نماید). با استفاده از این آبجکت می توان، به عنوان مثال، لیست پارامترهای یک متد را با مقادیر پر کرد.
Fake object ها / توابع ساختگی دارای پیاده سازی و قابلیت های واقعی اما معمولا بسیار ساده هستند. به عنوان مثال، بجای استفاده از دیتابیس واقعی از یک دیتابیس مقیم در حافظه (in-memory) استفاده می کنند.
پیش از توضیح مفهوم stub class، بد نیست به شرح مفهوم test stub بپردازیم. Test stub یک قطعه برنامه است که قابلیت های ضروری (که کامپوننت مورد تست به آن ها وابسته هست) از کامپوننت مورد تست را شبیه سازی می کند. test stub ها در جواب توابع صدا زده شده در طول تست، پاسخ های معین ارائه می دهند.
به عبارت دیگر، stub ها برنامه هایی هستند که به منزله ی یک جایگزین موقتی برای کامپوننت فراخوانی شده ایفای نقش کرده و همان خروجی نرم افزار یا برنامه ی واقعی را تولید می کنند.
Stub class (کلاس جایگزین با پیاده سازی نیمه) ، عبارت است از implementation ناتمام از کل یک کلاس یا interface که نمونه ای از آن در طول تست گیری مورد استفاده قرار می گیرد. stub ها ممکن است اطلاعاتی درباره ی فراخوانی های رخ داده نیز ثبت نمایند.
حال به شرح مفهوم mock object (آبجکت ساختگی) می پردازیم. Mock object ابجکت های ساختگی هستند که رفتار آبجکت های واقعی را به صورت کنترل شده و مشخص شبیه سازی و به عبارتی تقلید می کنند. برنامه نویس اغلب با استفاده از آبجکت ساختگی، سعی دارد تا رفتار آبجکت (واقعی) دیگری را شبیه سازی نماید.
به تعریف دیگر، mock object، عبارت است از پیاده سازی ساختگی برای یک interface یا کلاس که در آن خروجی فراخوانی برخی توابع تعریف می شود.
می توان test double ها را به آبجکت های مورد تست ارسال کرد (test double یک واژه ی عمومی برای تمامی توابع و آبجکت های ساختگی است که به منظور تست نرم افزار و کنترل کیفیت آن مورد استفاده قرار می گیرند). تست های شما می توانند بررسی کنند آیا کلاس مورد نظر در طول اجرای تست طبق انتظار پاسخ می دهند یا خیر.
به طور مثال شما در طول تست می توانید بررسی کنید، آیا توابع مورد نظر در سطح آبجکت به درستی فراخوانی می شوند یا خیر. این قابلیت به شما امکان می دهد تا اطمینان حاصل نمایید به هنگام تست گیری، منحصرا کلاس تست شده و اینکه تست های شما وابستگی به بخش های دیگر برنامه نداشته باشند (یا در نتیجه ی اجرای تست سایر بخش های پروژه تاثیر نپذیرند و عوارض جانبی درکار نباشد).
توسعه دهنده این امکان را دارد که آّبجکت های ساختگی (mock) را به صورت اختصاصی تنظیم کند. گفتنی است که آبجکت های ساختگی به کد و تنظیمات کمتری احتیاج داشته و بایستی در اجرای تست بر روی نرم افزار بر سایر روش ها ارجحیت داشته باشند.
نحوه ی ساخت mock object
مثالی شناخته شده و معمول از mock object، یک data provider ساده هست. در مرحله ی production یا استفاده ی واقعی از اپلیکیشن، برای اتصال به data source، پیاده سازی و کد لازم مورد استفاده قرار می گیرد. این در حالی است که برای تست، یک آبجکت ساختگی منبع داده ای را شبیه سازی نموده و اطمینان حاصل می نماید که شرایط تست همواره ثابت هستند.
این آبجکت های ساختگی را می توان در اختیار کلاس مورد تست قرار داد. به همین جهت، کلاسی که تست می شود نباید وابستگی (dependency) زیادی به داده های خارجی داشته باشد.
فریم ورک های شبیه سازی (Mocking framework) در کل این امکان را فراهم می آورند تا تعامل مورد انتظار با آبجکت ساختگی (mock) را به راحتی تست نمایید. به عنوان مثال، شما می توانید بررسی کنید که تنها متدهای مد نظر بر روی آبجکت ساختگی فراخوانی شده اند.
استفاده از Mockito برای شبیه سازی و ایجاد آبجکت ساختگی
Mockito یک فریم ورک پرطرفدار شبیه سازی و ایجاد آبجکت ساختگی برای تست نرم افزار است که گاه همراه با JUnit نیز مورد استفاده قرار می گیرد. این فریم ورک توسعه دهنده را قادر می سازد آّبجکت های ساختگی ایجاد کرده و رفتار یا پیاده سازی آن ها را بر اساس نیاز تنظیم کند. در واقع توسعه دهنده با بهره گیری از Mockito می تواند تست نویسی برای کلاس هایی که به سایر کلاس ها و کتابخانه ها وابسته هستند یا به عبارتی دیگر dependency به داده های خارجی دارند را بسیار آسان نماید. در استفاده از Mockito در تست های نرم افزاری خود، اغلب اقداماتی نظیر عنوان شده در زیر را انجام خواهید داد:
- تمامی Dependency ها به خارج از کلاس مورد تست (کلاس هایی که کلاس مورد تست با آن ها تعامل دارد) را شبیه سازی نموده و سپس آبجکت های ساختگی خود را داخل کد مورد تست درج می نمایید.
- کد مورد تست را اجرا نمایید.
- اطمینان حاصل نمایید که کد مورد نظر به درستی اجرا می شود.
افزودن Mockito در قالب dependency به پروژه
استفاده از سیستم کامپایل Gradle
برای کامپایل پروژه با استفاده از Gradle، لازم است ابتدا کتابخانه (dependency) زیر را به فایل build Gradle اضافه نمایید.
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:2.0.57-beta" }
استفاده از سیستم کامپایل Maven
کاربران سیستم Maven می توانند خود یک dependency تعریف کنند. عبارت های g:"org.mockito"، a:"mockito-core" را در وب سایت http://search.maven.org جستجو نموده و فایل pom مورد نیاز را پیدا نمایید.
استفاده از محیط کاری Eclipse
محیط برنامه نویسی Eclipse با هر دو سیستم Gradle و Maven سازگار می باشد. لازم به ذکر است که آخرین ویرایش فریم ورک Mockito امکان دانلود ابزار هر دو سیستم را نمی دهد. بنابراین توصیه می شود در این محیط کاری از مجموعه ابزار یکی از دو سیستم مزبور استفاده نمایید.
افزونه نویسی برای Eclipse یا OSGi (چارچوب توسعه و نصب کتابخانه ها و کامپوننت های نرم افزاری)
در اپلیکیشن های RCP یا برنامه های تحت وب سمت کلاینت مبتنی بر Eclipse، کتابخانه ها و dependency های مورد نیاز غالبا از مخزن پروژه ی p2 repository گرفته می شوند. Repository های Orbit هم منبع خوبی برای کتابخانه های کمکی (third party) هستند که به راحتی در اپلیکیشن ها یا افزونه های مبتنی بر Eclipse می توانید از آن ها استفاده نمایید.
برای دسترسی به repository های Orbit می توانید به آدرس http://download.eclipse.org/tools/orbit/downloads مراجعه نمایید.
استفاده از توابع کتابخانه ای Mockito / Mockito API
دستورات Static import
اگر به دستور org.mockito.Mockito.*;، یک static import اضافه نمایید، در آن صورت قادر خواهید بود به متدهای Mockitos مانند mock() به طور مستقیم دسترسی داشته باشید. static import ها به شما این امکان را می دهند تا اعضای static کلاس همچون متدها و فیلدها را بدون اینکه اسم کلاس را به طور صریح ذکر کنید، فراخوانی و استفاده نمایید.
اعلان و تنظیم آبجکت های ساختگی /mock objects با فریم ورک Mockito
همان طور که قبلا نیز ذکر شد، Mockito یک فریم ورک است که امکان تعریف آبجکت های ساختگی (mock object) را فراهم می آورد. برای این منظور، فریم ورک نام برده دو روش زیر را ارائه می دهد:
- فراخوانی تابع mock()
- استفاده از دستور @Mock
در صورت استفاده از روش دوم، شما بایستی خود آبجکت های ساختگی را مقداردهی اولیه نمایید. جهت مقداردهی اولیه این آبجکت لازم است از MockitoRule استفاده کنید. MockitoRule متد static ای به نام MockitoAnnotations.initMocks(this) را فراخوانی می کند که فیلدهای نشانه گذاری شده (دارای annotation) را مقداردهی می نماید. در صورت تمایل می توانید از دستور @RunWith(MockitoJUnitRunner.class) استفاده کرده و همین نتیجه را بدست بیاورید.
کد زیر استفاده از دستور @Mock و MockitoRule را به صورت عملی نمایش می دهد.
import static org.mockito.Mockito.*;
public class MockitoTest {
@Mock 1)
MyDatabase databaseMock;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 2)
@Test
public void testQuery() {
ClassToTest t = new ClassToTest(databaseMock); 3)
boolean check = t.query("* from t"); 4)
assertTrue(check); 5)
verify(databaseMock).query("* from t"); 6)
}
}
- به Mockito اعلان می کند که از نمونه ی databaseMock شبیه سازی یا mock کند.
- به Mockito دستور می دهد تا بر مبنای دستور @Mock، آبجکت های ساختگی یا mock ایجاد کند.
- با استفاده از آبجکت ساختگی از کلاس مورد تست نمونه سازی (instantiate) می کند.
- بخشی از کد کلاس مورد تست را اجرا می کند.
- انتظار دارد (assert می کند) که متد فراخوانده شده در خروجی مقدار بولی true را باز گرداند.
- بررسی کرده و اطمینان حاصل می کند که متد query بر روی mock یا نمونه ی ساختگی از MyDatabase فراخوانی می شود.
تنظیم آّبجکت های ساختگی / mock ها
می توانید به طور دقیق مشخص کنید که یک متد پس از فراخوانی، چه مقداری را در خروجی بازگرداند و این کار را به واسطه ی تکنیک نوشتن روان، خوانا و پشت سرهم دستورات یا همان fluent API که فریم ورک Mockito در اختیار شما قرار می دهد، انجام دهید.
دو متد when(….).thenReturn(….) که به صورت زنجیره ای و پشت سرهم فراخوانی می شوند این امکان را به شما می دهد تا یک شرط معین تعریف کرده و مقدار بازگشتی آن را نیز مشخص نمایید. اگر بیش از یک مقدار مشخص کنید، این مقادیر دقیقا به همان ترتیبی که تعریف شده اند، برگردانده می شوند. mock ها ممکن است بر اساس پارامترهای ارسالی به متد، مقادیر متفاوتی را بازگردانی نمایند. همچنین می توانید با استفاده از توابع anyString یا anyInt تعیین کنید که مستقل از مقدار ورودی، مقدار خاصی به عنوان خروجی از متد بازگردانده شود.
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
@Test
public void test1() {
// create mock
MyClass test = Mockito.mock(MyClass.class);
// define return value for method getUniqueId()
when(test.getUniqueId()).thenReturn(43);
// use mock in test....
assertEquals(test.getUniqueId(), 43);
}
// Demonstrates the return of multiple values
@Test
public void testMoreThanOneReturnValue() {
Iterator i= mock(Iterator.class);
when(i.next()).thenReturn("Mockito").thenReturn("rocks");
String result=i.next()+" "+i.next();
//assert
assertEquals("Mockito rocks", result);
}
// this test demonstrates how to return values based on the input
@Test
public void testReturnValueDependentOnMethodParameter() {
Comparable c= mock(Comparable.class);
when(c.compareTo("Mockito")).thenReturn(1);
when(c.compareTo("Eclipse")).thenReturn(2);
//assert
assertEquals(1,c.compareTo("Mockito"));
}
// this test demonstrates how to return values independent of the input value
@Test
public void testReturnValueInDependentOnMethodParameter() {
Comparable c= mock(Comparable.class);
when(c.compareTo(anyInt())).thenReturn(-1);
//assert
assertEquals(-1 ,c.compareTo(9));
}
// return a value based on the type of the provide parameter
@Test
public void testReturnValueInDependentOnMethodParameter() {
Comparable c= mock(Comparable.class);
when(c.compareTo(isA(Todo.class))).thenReturn(0);
//assert
Todo todo = new Todo(5);
assertEquals(todo ,c.compareTo(new Todo(1)));
}
دو تابع doReturn(…).when(…).methodCall نیز که به صورت پشت سرهم (زنجیره ای/chained) صدا زده می شوند مشابه نمونه ی قبلی عمل می کنند، اما بیشتر برای متدهایی که خروجی ندارند (void هستند)، کارا هستند. می توان با استفاده از doThrow برای متدهایی که خروجی ندارند (void برمی گردانند)، یک خطا (exception) صادر کرد. این کاربرد در تکه کد زیر به نمایش گذاشته می شود.
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
// this test demonstrates how use doThrow
@Test(expected=IOException.class)
public void testForIOException() {
// create an configure mock
OutputStream mockStream = mock(OutputStream.class);
doThrow(new IOException()).when(mockStream).close();
// use mock
OutputStreamWriter streamWriter= new OutputStreamWriter(mockStream);
streamWriter.close();
}
متد verify() (بررسی صحت فراخوانی یک متد با پارامترهای مورد نظر)
Mockito تمامی توابع فراخوانده شده و پارامترهای ارسالی به آبجکت ساختگی (mock) را رصد کرده و اطلاعات مربوط به آن ها را ثبت می کند. شما می توانید با استفاده از متد verify() بر روی آبجکت ساختگی مطمئن شوید که شرایط و نیاز های تعیین شده برآورده شده اند. به طور مثال، می توانید بررسی کرده و اطمینان حاصل نمایید که یک متد با پارامترهای مورد نیاز فراخوانی شده اند یا خیر. این تست گیری در اصطلاح behavior testing خوانده می شود. behavior testing با نتیجه ی متد فراخوانی شده کاری ندارد، بلکه می خواهد مطمئن شود آیا متد معینی با پارامترهای مورد نظر فراخوانی می شود یا خیر.
import static org.mockito.Mockito.*;
@Test
public void testVerify() {
// create and configure mock
MyClass test = Mockito.mock(MyClass.class);
when(test.getUniqueId()).thenReturn(43);
// call method testing on the mock with parameter 12
test.testing(12);
test.getUniqueId();
test.getUniqueId();
// now check if method testing was called with the parameter 12
verify(test).testing(Matchers.eq(12));
// was the method called twice?
verify(test, times(2)).getUniqueId();
// other alternatives for verifiying the number of method calls for a method
verify(mock, never()).someMethod("never called");
verify(mock, atLeastOnce()).someMethod("called at least once");
verify(mock, atLeast(2)).someMethod("called at least twice");
verify(mock, times(5)).someMethod("called five times");
verify(mock, atMost(3)).someMethod("called at most 3 times");
}
قرار دادن آبجکت های جاوا در Spy (رصد و شنود آبجکت های جاوا و توابع فراخوانده شده بر روی آن با spy)
می توانید با درج دستور @Spy یا فراخوانی متد spy()، آبجکت های (واقعی) جاوا را رصد نمایید. بدین معنی که ابتدا آبجکت را داخل دو دستور ذکر شده قرار دهید، سپس تمامی توابع متعارف آبجکت را صدا زده و در این میان هر اتفاق یا تعاملی که رخ می دهد را دقیقا ردگیری (شنود و دنبال) کنید. در واقع با این کار تمامی توابع، به جز آن هایی که خود به صورت صریح مستثنی نمایید، بر روی این آبجکت صدا زده می شوند.
import static org.mockito.Mockito.*;
// Lets mock a LinkedList
List list = new LinkedList();
List spy = spy(list);
//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
// this would not work
// real method is called so spy.get(0)
// throws IndexOutOfBoundsException (list is still empty)
when(spy.get(0)).thenReturn("foo");
متد verifyNoMoreInteractions() به شما این امکان را می دهد تا مطمئن شوید هیچ تابع دیگری (به غیر از آنچه خود به صورت صریح مشخص کرده اید) فراخوانی نشده است.
پیاده سازی الگوی dependency injection در فریم ورک Mockito با استفاده از دستور @InjectMocks
فریم ورک Mockito با ارائه ی دستوری به نام @InjectMocks به شما این امکان را می دهد تا بر اساس نوع dependency مورد نظر، آن را از طریق تابع سازنده (constructor)، متد یا فیلد تزریق نمایید (الگوی dependency injection را به این صورت پیاده سازی کنید).
public class ArticleManager {
private User user;
private ArticleDatabase database;
ArticleManager(User user) {
this.user = user;
}
void setDatabase(ArticleDatabase database) { }
}
این کلاس را می توان از طریق Mockito ساخته و کلاس های مورد نیاز یا dependency های آن را با استفاده از آبجکت های ساختگی (mocks) به داخل آن تزریق کرد.
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock ArticleCalculator calculator;
@Mock ArticleDatabase database;
@Most User user;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks private ArticleManager manager; 1)
@Test public void shouldDoSomething() {
// assume that ArticleManager has a method called initialize which calls a method
// addListener with an instance of ArticleListener
manager.initialize();
// validate that addListener was called
verify(database).addListener(any(ArticleListener.class));
}
}
یک نمونه از کلاس ArticleManager ساخته و آّبجکت های ساختگی (mock ها) را به داخل آن تزریق می کند.
دسترسی به آرگومان های ارسالی به توابع (Capturing arguments)
کلاس ArgumentCaptor به شما امکان می دهد تا به آرگومان های پاس داده به متدها در طول پروسه ی بررسی صحت فراخوانی متد، دسترسی داشته باشید. بدین وسیله شما قادر خواهید بود به آرگومان های توابع فراخوانی شده دسترسی داشته و سپس آن ها را در تست های خود مورد استفاده قرار دهید.
import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
public class MockitoTests {
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Captor
private ArgumentCaptor<>> captor;
@Test
public final void shouldContainCertainListItem() {
List asList = Arrays.asList("someElement_test", "someElement");
final List mockedList = mock(List.class);
mockedList.addAll(asList);
verify(mockedList).addAll(captor.capture());
final List capturedArgument = captor.<>>getValue();
assertThat(capturedArgument, hasItem("someElement"));
}
}
محدودیت های فریم ورک Mockito
فریم ورک Mockito نیز مانند سایر چارچوب های کاری محدودیت هایی دارد. این فریم ورک قادر به تست ساختارهای زیر نمی باشد:
- کلاس هایی که به صورت final تعریف شده باشند و امکان ارث بری از آن ها وجود نداشته باشد
- کلاس های anonymous یا کلاس های فاقد اسم
- انواع داده ای اولیه و primitive همچون string
استفاده از فریم ورک Mockito در اندروید
Mockito را می توان به طور مستقیم نیز در unit test های اندروید مورد استفاده قرار داد. کافی است dependency مربوطه را به فایل Gradle build اضافه نمایید. جهت استفاده از این فریم ورک در تست های instrumentation اندروید، لازم است dexmaker و dexmaker-mockito را نیز به عنوان dependency در فایل Gradle build اضافه نمایید.
dependencies {
testCompile 'junit:junit:4.12'
// required if you want to use Mockito for unit tests
testCompile 'org.mockito:mockito-core:1.+'
// required if you want to use Mockito for Android instrumentation tests
androidTestCompile 'org.mockito:mockito-core:1.+'
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
}
تمرین: نوشتن تست مبتنی بر instrumentation با استفاده از فریم ورک Mockito
ایجاد اپلیکیشن های آزمایشی (مورد تست) در اندروید
یک اپلیکیشن جدید اندروید با اسم پکیج com.vogella.android.testing.mockito.contextmock تعریف نمایید. حال یک متد static داخل اعلان نموده و در بدنه ی آن یک آبجکت intent با پارامترهای زیر ایجاد نمایید.
public static Intent createQuery(Context context, String query, String value) {
// Reuse MainActivity for simplification
Intent i = new Intent(context, MainActivity.class);
i.putExtra("QUERY", query);
i.putExtra("VALUE", value);
return i;
}
اضافه کردن dependency مورد نیاز (Mockito) به فایل app/build.gradle
dependencies {
// the following is required to use Mockito and JUnit for your
// instrumentation unit tests on the JVM
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'org.mockito:mockito-core:2.0.57-beta'
androidTestCompile 'com.android.support.test:runner:0.3'
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
// the following is required to use Mockito and JUnit for your unit
// tests on the JVM
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.+'
}
ساخت یک unit test جدید
اکنون یک unit test جدید با استفاده از Mockito ایجاد نموده و بررسی کنید آیا intent با داده های extra مرتبط فراخوانی می شود یا خیر.
برای این منظور آبجکت Context را با استفاده از توابع Mockito به صورت زیر شبیه سازی نمایید.
package com.vogella.android.testing.mockitocontextmock;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class TextIntentCreation {
@Test
public void testIntentShouldBeCreated() {
Context context = Mockito.mock(Context.class);
Intent intent = MainActivity.createQuery(context, "query", "value");
assertNotNull(intent);
Bundle extras = intent.getExtras();
assertNotNull(extras);
assertEquals("query", extras.getString("QUERY"));
assertEquals("value", extras.getString("VALUE"));
}
}
تمرین: ساخت آّبجکت های ساختگی (mock) با استفاده از توابع Mockito
هدف از تمرین
در تمرین حاضر، ابتدا API ای تعبیه نمایید که قابل تست و شبیه سازی باشند. سپس با استفاده از توابع Mockito آن ها را تست نمایید.
ایجاد یک نمونه Twitter API
ابتدا یک کلاس TwitterClient پیاده سازی کنید که از نمونه ای از اینترفیس ITweet استفاده کرده و توابع آن را فراخوانی می کند. دسترسی به نمونه های این interface ملال آور بوده و نیازمند استفاده از یک سرویس هزینه بر و پیچیده می باشد. علاوه بر این، سرویس مذکور بایستی برای دسترسی، اول فراخوانی و اجرا شود.
public interface ITweet {
String getMessage();
}
public class TwitterClient {
public void sendTweet(ITweet tweet) {
String message = tweet.getMessage();
// send the message to Twitter
}
}
شبیه سازی از نمونه ی ITweet
برای اینکه لازم نباشد جهت دسترسی به نمونه هایی از اینترفیس ITweet، یک سرویس هزینه بر و پیچیده راه اندازی نمایید، توصیه می شود نمونه ی interface مزبور را به واسطه ی فریم ورک Mockito شبیه سازی کنید.
@Test
public void testSendingTweet() {
TwitterClient twitterClient = new TwitterClient();
ITweet iTweet = mock(ITweet.class);
when(iTweet.getMessage()).thenReturn("Using mockito is great");
twitterClient.sendTweet(iTweet);
}
اکنون کلاس TwitterClient می تواند توابع اینترفیس ساختگی و شبیه سازی شده ی ITweet را پیاده سازی نماید. در نتیجه ی استفاده از توابع این interface شبیه سازی شده (فراخوانی متد getMessage() بر روی نمونه ی ساختگی ITweet)، پیغام "Using Mockito is great" به عنوان خروجی متد در اختیار شما قرار می گیرد.
بررسی صحت فراخوانی متد
اطمینان حاصل نمایید که متد getMessage() حداقل یکبار فراخوانی می شود.
@Test
public void testSendingTweet() {
TwitterClient twitterClient = new TwitterClient();
ITweet iTweet = mock(ITweet.class);
when(iTweet.getMessage()).thenReturn("Using mockito is great");
twitterClient.sendTweet(iTweet);
verify(iTweet, atLeastOnce()).getMessage();
}
مرحله ی اعتبار سنجی تست
تست را اجرا کرده و مطمئن شوید که با موفقیت انجام می شود.
استفاده از PowerMock به همراه Mockito
استفاده از PowerMock برای شبیه سازی متدهای static
فریم ورک Mockito قادر به شبیه سازی متدهای static نیست. برای شبیه سازی رفتار متدهای static ، بایستی از PowerMock استفاده نمایید. PowerMock یک کلاس به نام "PowerMockito" ارائه می دهد که برای ساخت mock/object/class، بررسی صحت اجرا(اعتبارسنجی) و برآورده ساختن انتظارات این کلاس را فراخوانی می کند.
برای بررسی صحت برآورده شدن انتظارات می توانید همچنان از خود Mockito (توابع times() ، anyInt()) استفاده نمایید.
import java.net.InetAddress;
import java.net.UnknownHostException;
public final class NetworkReader {
public static String getLocalHostname() {
String hostname = "";
try {
InetAddress addr = InetAddress.getLocalHost();
// Get hostname
hostname = addr.getHostName();
} catch ( UnknownHostException e ) {
}
return hostname;
}
}
برای نوشتن تستی که قابلیت های NetworkReader را برای شما شبیه سازی می کند، می توانید از تکه کد زیر استفاده نمایید.
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
@RunWith( PowerMockRunner.class )
@PrepareForTest( NetworkReader.class )
public class MyTest {
// Find the tests here
@Test
public void testSomething() {
mockStatic( NetworkUtil.class );
when( NetworkReader.getLocalHostname() ).andReturn( "localhost" );
// now test the class which uses NetworkReader
}
همچنین شما می توانید تابع static دلخواه خود را داخل یک wrapper گنجانده (یک تابع که تابع دیگری را فراخوانی می کند) و سپس آن را توسط Mockito شبیه سازی نمایید.
class FooWraper {
void someMethod() {
Foo.someStaticMethod()
}
}