目次
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<String> 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<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 }
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<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(); } }
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)を実装している。