====== Mocking Example ====== %%PowerMock%%を利用したexampleを残しておく。 ===== Mocking Constructor ===== target classのコードの一部を次に示す。 public class SomeActionForm extends AbstractActionForm { ~~中略~~ //***** public method ***** @Override public ActionErrors validate(ActionMapping arg0, HttpServletRequest arg1) { ActionErrors _errors = new ActionErrors(); //do something return _errors; } } このクラスは、struts1の%%ActionForm%%クラスの一部である。\\ 異常系テストとして、validationエラーメッセージを確認するために、%%ActionErrors%%クラスをmockしたいケースである。\\ mockitoでは、constructorのmockクラスを作成できないため、%%PowerMock%%を利用する。\\ テストクラスコードの一部を次に示す。 @PrepareForTest({SomeActionForm.class}) public class TestSomeActionForm extends AbstractScreenTestCase { ~~中略 private void doCommonValidate(SomeActionForm form, List errorCodeList) throws Exception { ~~中略 //mock ActionErrors ActionErrors _mockActionErrors = mock(ActionErrors.class); //new instanceで生成されるActionErrosインスタンスをmock objectに変換 whenNew(ActionErrors.class).withNoArguments().thenReturn(_mockActionErrors); ~~中略 } } ここで注意したい部分は、%%@PrepareForTest%%だ。\\ テスト対象クラスではなく、mock対象のConstructorが存在するクラスを指定する。\\ 複数個所がある場合は、カンマ区切りで指定する。 これだけでは、%%ActionErrors%%のエラーメッセージが確認できない。\\ 他に%%ArgumetCaptor%%が必要なのだが、次節で説明する。 ===== ArgumentCaptor ===== 前節の続きで、%%ActionErrors%%のエラーメッセージを確認するコードを次に示す。 private void doCommonValidate(SomeActionForm form, List errorCodeList) throws Exception { //create Captor instances for ActionError parameters that will be added to the ActionErrors ArgumentCaptor _actionErrorCaptor = ArgumentCaptor.forClass(ActionError.class); //mock ActionErrors ActionErrors _mockActionErrors = mock(ActionErrors.class); //new instanceで生成されるActionErrosインスタンスをmock objectに変換 whenNew(ActionErrors.class).withNoArguments().thenReturn(_mockActionErrors); //call test method form.validate(actionMapping, request); //verify if the call add() to ActionErrors was made //and capture the ActionError that was passed verify(_mockActionErrors, atLeastOnce()).add(anyString(), _actionErrorCaptor.capture()); //get the capture ActionError and check the set values List _errors = _actionErrorCaptor.getAllValues(); //show error message for (ActionError _error : _errors) { assertTrue(errorCodeList.contains(_error.getKey()), "{unexpected error code = [" + _error.getKey() + "]}"); System.out.println(rsc(_error.getKey(), _error.getValues())); }//for } %%ArgumentCaptor%%の基本使い方をを下に示す。\\ - 監視したいクラスを%%ArgumentCaptor%%に渡す。 - テストメソッドをcallする。 - verifyメソッドで、監視クラスがcallされたかcheckする。 - %%ArgumentCaptor%%クラスのgetAllValues(複数の場合)か、getValue(単一)を利用して監視クラスを出力する。 ===== Mocking static method ===== staticメソッドのmockクラスを作成したい場合は、mockStaticメソッドを利用する。\\ targetクラスの一部を下に示す。 public class DocumentShareSendMailService extends AbstractBaseSampleService { ~~中略 @Override protected void execute() { ~~中略 ExecutorService _threadPool = Executors.newFixedThreadPool(FIXED_THREAD_CNT); AsyncECabinetUploadUtil _util = new AsyncECabinetUploadUtil(_threadPool); ~~中略 } } %%AysncECabinetUploadUtil%%クラスは、%%ExecutorService%%を使って非同期処理を行うクラスであるが、テストを実行しても実行されてないことに気づく。\\ 本処理では、非同期処理だが、テストの際は同期処理にしたいというケースで、%%ExecutorService%%ではなく、guavaモジュールに含まている%%MoreExecutors%%クラスを利用する。 テストクラスのコード一部を次に示す。 @PrepareForTest({DocumentShareSendMailService.class}) public class TestDocumentShareSendMailService extends AbstractCustomTestCase { ~~中略 @Override protected void prepareSpecific() throws Exception { //mock Executors mockStatic(Executors.class); //wait until tasks are completed ExecutorService _mockExecutor = MoreExecutors.newDirectExecutorService(); when(Executors.newFixedThreadPool(anyInt())).thenReturn(_mockExecutor); } } %%newDirectExecutorService%%メソッドがすぐにtaskを実行する%%ExecutorService%%を返すため、テスト結果が確認できる。 ===== Using an ArgumentCaptor of Collection type ===== %%ArgumentCaptor%%にList型を使いたい場合、%%@Captor%%と%%MockitoAnnotations.initMocks%%を利用する。 次にテストクラスの一部を示す。 public class TestPersonService { @Captor ArgumentCaptor> captor @Before public void before() { MockitoAnnotations.initMocks(this); } @Test public void testService001() { PersonService service = ... ; service.execute(); verify(service).update(captor.capture()); assertThat(captor.getValue()).isEmpty(); } } ===== Mock Protected Parent Method ===== Parent classにあるprotected methodの振る舞いを定義する例を紹介する。 親クラスの例を次に示す。 package parent; public class Parent { protected void foo(String arg1, String arg2) { //some logic here } } 子クラスの例を次に示す。 package child; import parent.Parent; public class Child extends Parent { public String bar() { //call parent method this.foo(); //Child logic here } } テストクラスの例を次に示す。 package child; import parent.Parent; import static org.mockito.Matchers.anyObject; import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.when; import org.powermock.core.classloader.annotations.PrepareForTest; @PrepareForTest({Parent.class, Child.class}) public class ChildTest { //Class Under Test Child cut; @Before public void setUp() throws Exception { //Partial mock to mock methods in parent class cut = spy(new Child()); //stub parent foo method doNothing().when(cut, "foo", anyObject(), anyObject()); } @Test public void testBar() { //call test method cut.bar(); //assertion here } } ここでは、親クラスのfooメソッドが呼ばれた際、何もしない(doNothing)を実装している。