差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
study:java:powermock:mocking [2020/03/26 08:02] – [Mocking Constructor] bananastudy: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) { 
 +        ActionErrors _errors = new ActionErrors(); 
 + 
 +        //do something 
 + 
 +        return _errors; 
 +    } 
 +}
 </code> </code>
 +
 +このクラスは、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<String> errorCodeList) throws Exception {
 +        ~~中略
 +        //mock ActionErrors
 +        ActionErrors _mockActionErrors = mock(ActionErrors.class);
 +        //new instanceで生成されるActionErrosインスタンスをmock objectに変換
 +        whenNew(ActionErrors.class).withNoArguments().thenReturn(_mockActionErrors);
 +        ~~中略
 +    }
 +}
 +</code>
 +
 +ここで注意したい部分は、%%@PrepareForTest%%だ。\\
 +テスト対象クラスではなく、mock対象のConstructorが存在するクラスを指定する。\\
 +複数個所がある場合は、カンマ区切りで指定する。
 +
 +これだけでは、%%ActionErrors%%のエラーメッセージが確認できない。\\
 +他に%%ArgumetCaptor%%が必要なのだが、次節で説明する。
 +
 +===== ArgumentCaptor =====
 +前節の続きで、%%ActionErrors%%のエラーメッセージを確認するコードを次に示す。
 +<code java>
 +    private void doCommonValidate(SomeActionForm form, List<String> errorCodeList) throws Exception {
 +        //create Captor instances for ActionError parameters that will be added to the ActionErrors
 + ArgumentCaptor<ActionError> _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<ActionError> _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
 +    }
 +</code>
 +
 +%%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);
 +        ~~中略
 +    }
 +}
 +</code>
 +%%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);
 +    }
 +}
 +</code>
 +%%newDirectExecutorService%%メソッドがすぐにtaskを実行する%%ExecutorService%%を返すため、テスト結果が確認できる。
 +
 +===== Using an ArgumentCaptor of Collection type  =====
 +%%ArgumentCaptor%%にList型を使いたい場合、%%@Captor%%と%%MockitoAnnotations.initMocks%%を利用する。
 +
 +次にテストクラスの一部を示す。
 +<code java>
 +public class TestPersonService {
 +    @Captor
 +    ArgumentCaptor<List<Person>> 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();
 +    }
 +
 +}
 +</code>
 +
 +===== 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>
 +
 +子クラスの例を次に示す。
 +<code java>
 +package child;
 +
 +import parent.Parent;
 +
 +public class Child extends Parent {
 +
 +    public String bar() {
 +        //call parent method
 +        this.foo();
 +
 +        //Child logic here
 +    }
 +}
 +</code>
 +
 +テストクラスの例を次に示す。
 +<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, 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
 +    }
 +}
 +
 +</code>
 +ここでは、親クラスのfooメソッドが呼ばれた際、何もしない(doNothing)を実装している。
 +

QR Code
QR Code study:java:powermock:mocking (generated for current page)