====== 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)を実装している。