差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
| study:java:powermock:mocking [2020/03/26 08:02] – [Mocking Constructor] banana | study:java:powermock:mocking [2020/05/12 01:19] (現在) – [Mock Protected Parent Method] banana | ||
|---|---|---|---|
| 行 5: | 行 5: | ||
| target classのコードの一部を次に示す。 | target classのコードの一部を次に示す。 | ||
| <code java> | <code java> | ||
| - | ActionErrors _errors = new ActionErrors(); | + | public class SomeActionForm extends AbstractActionForm { |
| + | ~~中略~~ | ||
| + | //***** public method ***** | ||
| + | @Override | ||
| + | public ActionErrors validate(ActionMapping arg0, HttpServletRequest arg1) { | ||
| + | | ||
| + | |||
| + | //do something | ||
| + | |||
| + | return _errors; | ||
| + | } | ||
| + | } | ||
| </ | </ | ||
| + | |||
| + | このクラスは、struts1の%%ActionForm%%クラスの一部である。\\ | ||
| + | 異常系テストとして、validationエラーメッセージを確認するために、%%ActionErrors%%クラスをmockしたいケースである。\\ | ||
| + | mockitoでは、constructorのmockクラスを作成できないため、%%PowerMock%%を利用する。\\ | ||
| + | |||
| + | テストクラスコードの一部を次に示す。 | ||
| + | <code java> | ||
| + | @PrepareForTest({SomeActionForm.class}) | ||
| + | public class TestSomeActionForm extends AbstractScreenTestCase { | ||
| + | ~~中略 | ||
| + | private void doCommonValidate(SomeActionForm form, List< | ||
| + | ~~中略 | ||
| + | //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%%のエラーメッセージを確認するコードを次に示す。 | ||
| + | <code java> | ||
| + | private void doCommonValidate(SomeActionForm form, List< | ||
| + | //create Captor instances for ActionError parameters that will be added to the ActionErrors | ||
| + | ArgumentCaptor< | ||
| + | |||
| + | //mock ActionErrors | ||
| + | ActionErrors _mockActionErrors = mock(ActionErrors.class); | ||
| + | //new instanceで生成されるActionErrosインスタンスをmock objectに変換 | ||
| + | whenNew(ActionErrors.class).withNoArguments().thenReturn(_mockActionErrors); | ||
| + | |||
| + | //call test method | ||
| + | form.validate(actionMapping, | ||
| + | |||
| + | //verify if the call add() to ActionErrors was made | ||
| + | //and capture the ActionError that was passed | ||
| + | verify(_mockActionErrors, | ||
| + | |||
| + | //get the capture ActionError and check the set values | ||
| + | List< | ||
| + | |||
| + | //show error message | ||
| + | for (ActionError _error : _errors) { | ||
| + | assertTrue(errorCodeList.contains(_error.getKey()), | ||
| + | System.out.println(rsc(_error.getKey(), | ||
| + | }//for | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | %%ArgumentCaptor%%の基本使い方をを下に示す。\\ | ||
| + | - 監視したいクラスを%%ArgumentCaptor%%に渡す。 | ||
| + | - テストメソッドをcallする。 | ||
| + | - verifyメソッドで、監視クラスがcallされたかcheckする。 | ||
| + | - %%ArgumentCaptor%%クラスのgetAllValues(複数の場合)か、getValue(単一)を利用して監視クラスを出力する。 | ||
| + | |||
| + | ===== Mocking static method ===== | ||
| + | staticメソッドのmockクラスを作成したい場合は、mockStaticメソッドを利用する。\\ | ||
| + | targetクラスの一部を下に示す。 | ||
| + | <code java> | ||
| + | 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%%クラスを利用する。 | ||
| + | |||
| + | テストクラスのコード一部を次に示す。 | ||
| + | <code java> | ||
| + | @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%%を利用する。 | ||
| + | |||
| + | 次にテストクラスの一部を示す。 | ||
| + | <code java> | ||
| + | public class TestPersonService { | ||
| + | @Captor | ||
| + | ArgumentCaptor< | ||
| + | |||
| + | @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の振る舞いを定義する例を紹介する。 | ||
| + | |||
| + | 親クラスの例を次に示す。 | ||
| + | <code java> | ||
| + | package parent; | ||
| + | |||
| + | public class Parent { | ||
| + | |||
| + | protected void foo(String arg1, String arg2) { | ||
| + | //some logic here | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 子クラスの例を次に示す。 | ||
| + | <code java> | ||
| + | package child; | ||
| + | |||
| + | import parent.Parent; | ||
| + | |||
| + | public class Child extends Parent { | ||
| + | |||
| + | public String bar() { | ||
| + | //call parent method | ||
| + | this.foo(); | ||
| + | |||
| + | //Child logic here | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | テストクラスの例を次に示す。 | ||
| + | <code java> | ||
| + | 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, | ||
| + | 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, | ||
| + | } | ||
| + | |||
| + | @Test | ||
| + | public void testBar() { | ||
| + | //call test method | ||
| + | cut.bar(); | ||
| + | //assertion here | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | ここでは、親クラスのfooメソッドが呼ばれた際、何もしない(doNothing)を実装している。 | ||
| + | |||