ログ日記

作業ログと日記とメモ

Slim3 + GWT のユニットテストで mockito を使ってみる

Slim3 + GWT でクライアント側のテストをするために、Gin はちょっとオーバースペックな気がしたので mockito を使ってみてる。

    private ItemServiceImpl itemService = spy(new ItemServiceImpl());

    @Test
    public void myTest() throws Exception {
        ClientFactory clientFactory = mock(ClientFactory.class);
        when(clientFactory.getEventBus()).thenReturn(new SimpleEventBus());

        Activity topActivity = mock(Activity.class);
        when(clientFactory.getTopActivity()).thenReturn(topActivity);

        when(clientFactory.getItemService()).thenReturn(itemServiceAsync);


        ...

        topActivity.getItems();
        verify(itemService).getItems();

で、これのitemServiceAsyncなんだけども、これをどうやって作ろうかと。
コールバックのテストってどうやるんだろう?
最初はモックを使わずに new ItemServiceAsync(){ ... } とテストコードに書いていたんだけど、メソッドが増えたらいちいち書いてられない。


ちょっと迷った末にProxyを作ることにした。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.google.gwt.user.client.rpc.AsyncCallback;

public class AsyncServiceProxy {
    @SuppressWarnings("unchecked")
    public static <T1, T2> T1 getProxy(Class<T1> async, final T2 service) {
        return (T1) Proxy.newProxyInstance(
            AsyncServiceProxy.class.getClassLoader(),
            new Class[] {async},
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    Class<?>[] params = method.getParameterTypes();
                    Class<?>[] serviceParams = new Class<?>[params.length - 1];
                    for (int i = 0; i < params.length - 1; i++) {
                        serviceParams[i] = params[i];
                    }
                    Method serviceMethod = null;
                    try {
                        serviceMethod = service.getClass().getMethod(method.getName(), serviceParams);
                    } catch (SecurityException e) {
                        e.printStackTrace();
                        return null;
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                        return null;
                    }
                    Object[] serviceArgs = new Object[args.length - 1];
                    int i;
                    for (i = 0; i < args.length - 1; i++)
                        serviceArgs[i] = args[i];
                    AsyncCallback<Object> callback = (AsyncCallback<Object>) args[i];

                    Object ret = serviceMethod.invoke(service, serviceArgs);

                    callback.onSuccess(ret);
                    return null;
                }
        });
    }
}

やっつけ感満載のコードだけれども。
これで

        ItemServiceAsync itemServiceAsync = AsyncServiceProxy.getProxy(ItemServiceAsync.class, itemService);

と書けば AsyncService => ServiceImpl => AsyncCallback#onSuccess と変換して呼び出すコールバックが生成できる。


なんだかmockitoや他のライブラリにちゃんとしたやり方がありそうな気がするが、見つからなかったのでとりあえずこれで。


mockito 参考:
http://d.hatena.ne.jp/Naotsugu/20101109/1289304795
http://d.hatena.ne.jp/takahashikzn/20101001/1285897862
http://tech.cm55.com/wiki/mockito/General