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の基本使い方をを下に示す。

  1. 監視したいクラスをArgumentCaptorに渡す。
  2. テストメソッドをcallする。
  3. verifyメソッドで、監視クラスがcallされたかcheckする。
  4. 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)を実装している。


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