com.google.appengine.api.oauth.OAuthServiceのテスト

2013-08-26T00:00:00+00:00 Google App Engine

Slim3にて、com.google.appengine.api.oauth.OAuthServiceを使ってOAuthリクエストで認証されているかをチェックしているようなケースをコントローラーでチェックしているような場合においてテストをどうやるのかと

例えば/readっていうのに対してReadControllerが発生するような場合

public abstract class ShareController extends Controller {

    protected ShareService service = new ShareService();

    protected void protectOAuthRequest() throws IOException, OAuthRequestException {
        OAuthService oauthService = OAuthServiceFactory.getOAuthService();

        if (!oauthService.isUserAdmin()) {
            throw new OAuthRequestException("unauthorized");
        }
    }
}

というような基底抽象コントローラークラスがあってこれを継承してコントローラーを作る。で作った/readに対するReadControllerなテストをする場合

package shareroid.controller.test;

import org.junit.Before;
import org.junit.Test;
import org.slim3.datastore.Datastore;

import shareroid.controller.ReadController;
import shareroid.model.Share;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class ReadControllerTest extends AbstractControllerTestCase {
    @Test
    public void test_run() throws Exception {
        start("/read");
        assertThat(getController(), instanceOf(ReadController.class));
        assertThat(getStatus(), is(200));
        assertThat(getContentType(), is("application/json; charset=utf-8"));
    }
}

というようなテストをぶち込んだらどうなるか。もちろんテストはずっこける、OAuthService#isUserAdminでは無いので例外が発生して、handleErrorでキャッチ?されレスポンスが500で送出されるようになっている。そうなると普通にリクエストしてテストを実行してもテストずっこけるよねっていう事

でこういう場合ってどうやってテストすんのって所なんですが、Google App Engine/Javaの場合大概のService APIなパッケージには末尾に.devがついてるパッケージにおいてLocalなんちゃらService的なローカルでテストするためのヘルパーAPI的ながついてると思うんだけど、OAuthServiceなそれが無いっぽい。んじゃこれどうやってテストすんのかって思ってとりあえず「モック差し込んじゃば良いんじゃね?」って思ったのでやってみる。前おきが長かったねww

OAuthServiceを実装したMockOAuthServiceクラスを作る

※あくまでテスト側に作る。デプロイはしないように

package shareroid.controller.test;

import com.google.appengine.api.oauth.OAuthRequestException;
import com.google.appengine.api.oauth.OAuthService;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserServiceFactory;

public class MockOAuthService implements OAuthService {

    @Override
    public boolean isUserAdmin(String arg0) throws OAuthRequestException {
        return true;
    }

    @Override
    public boolean isUserAdmin() throws OAuthRequestException {
        return true;
    }

    @Override
    public String getOAuthConsumerKey() throws OAuthRequestException {
        return null;
    }

    @Override
    public User getCurrentUser(String arg0) throws OAuthRequestException {
        return null;
    }

    @Override
    public User getCurrentUser() throws OAuthRequestException {
        return UserServiceFactory.getUserService().getCurrentUser();
    }

    @Override
    public String getClientId(String arg0) throws OAuthRequestException {
        return null;
    }
}

まー、返す値とかは個人で調整すれば良いんじゃないのかと。今回isUserAdminメソッドくらいしか使ってないので微妙な所なんですが

テストを修正する

で何でモックを差し込むのか。まぁjmockitでという事で (めんどくさいので導入方法はググれ)

package shareroid.controller.test;

import mockit.Expectations;
import mockit.Mocked;

import org.junit.Test;

import com.google.appengine.api.oauth.OAuthServiceFactory;

import shareroid.controller.ReadController;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class ReadControllerTest extends AbstractControllerTestCase {
    @Test
    public void test_run() throws Exception {
        new Expectations() {

            @Mocked(methods = "getOAuthService")
            final OAuthServiceFactory factory = null;

            {
                OAuthServiceFactory.getOAuthService();
                times = 1;
                result = new MockOAuthService();
            };
        };

        start("/read");
        assertThat(getController(), instanceOf(ReadController.class));
        assertThat(getStatus(), is(200));
        assertThat(getContentType(), is("application/json; charset=utf-8"));
    }
}

というようにOAuthServiceFactory#getOAuthServiceなstaticメソッドから返される値をMockOAuthServiceに変えるだけ。で基底コントローラー上でgetOAuthServiceをSystem.out.println辺りしてみると

  • 差し込んでない場合: com.google.appengine.api.oauth.OAuthServiceImpl
  • 差し込んでる場合: shareroid.controller.test.MockOAuthService

というような方式を使えばテスト出来ないも無い感じなのかなと。他に良い解決方法があるのかは微妙だけれども

まーそんな感じで

mocha+coverjsでコードカバレッジ ざっくりとindexedDBを使ってみる