jmockit

2013-06-07T00:00:00+00:00 Java

最強(って言われてる気がする)なモックフレームワーク?な https://code.google.com/p/jmockit を触ってみた。しかもライブラリ入れるだけでカバレッジも出力出来るという(jmockit-coverage)

import org.junit.Test;

import mockit.Expectations;
import mockit.Invocation;
import mockit.Mock;
import mockit.MockUp;
import mockit.Mocked;

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

public class SampleTestCase {

    // 引数に何を指定しても5しか返さないようにメソッドをMock
    @Test
    public void test1() {
        new Expectations() {
            @Mocked(methods="pow")
            Sample sample;

            {
                sample.pow(anyInt);
                result = 5;
            }
        };


        Sample sample = new Sample();
        assertThat(sample.pow(3), is(5));
    }

    // メソッドを実行すると指定した例外が送出されるようにする
    @Test(expected=IllegalArgumentException.class)
    public void test2() {
        new Expectations() {
            @Mocked(methods="pow")
            Sample sample;

            {
                sample.pow(anyInt);
                result = new IllegalArgumentException();
            }
        };

        Sample sample = new Sample();
        sample.pow(2);
    }

    // staticメソッドから返される値をMock
    @Test
    public void test3() {
        new Expectations() {
            @Mocked(methods = "pow2")
            final Sample sample = null;

            {
                Sample.pow2(anyInt);
                result = 6;
            }
        };

        Sample sample = new Sample();
        assertThat(sample.pow(2), is(6));
     // powメソッドは実体がstaticなpow2メソッドを実行する
    }

    // pow2のstaticメソッドで使用しているMath.powをmock
    @Test
    public void test4() {
        new Expectations() {
            @Mocked(methods = "pow")
            final Math math = null;

            {
                Math.pow(anyDouble, anyDouble);
                result = 5;
            }
        };

        Sample sample = new Sample();
        assertThat(sample.pow(2), is(5));
    }

    // 引数をチェックしつつテストする場合とか。例えばなんかのメソッドを仲介する場合とか?
    // でInvocation#getInvokedInstanceでMockなクラスを取ってごにょごにょすれば元のを利用できる?
    @Test
    public void test5() {
        // Math.powが1を返す
        new Expectations() {
            @Mocked(methods = "pow")
            final Math math = null;

            {
                Math.pow(anyDouble, anyDouble);
                result = 1;
            }
        };

        // メソッドをspy?しつつオリジナルなメソッドをコールして結果をリターン
        new MockUp<Sample>() {
            @Mock
            int pow(Invocation inv, int n) {
                assertThat(n, is(2));

                return ((Sample)inv.getInvokedInstance()).pow(n);
            }
        };

        Sample sample = new Sample();
        assertThat(sample.pow(2), is(1));
    }
}

な感じ。特にtest5があれなんですけど、MockUpでメソッドをMock化する場合に引数にInvocationが入るオーバーロードメソッドがある模様。なのでInvocation#getInvokedInstanceを使う事でそのモック化されている元のクラスのインスタンスを取得する事が出来る。引数をspy?しつつ、結果をオリジナルなのを返したいとかそういう場合にはこういう風にやれば良いのかなと

あとfinalクラスでもMockを差し込めるっていうのが良いかもですね、Mockitoとかだと出来ないはずなので。個人的にはAndroidとかでも使えればなぁって思ったりする所ですが、Instrumentationな事情的な所でちょっと難しそうな感じ。Robolectric?とか使えば可能らしいですけどね、あれめんどくさい

んまぁドキュメントわかりにくくも無い(ちょっと読みづらいかな)と思うので、それ読めばオッケーかと

Android AccountManagerのgetAuthTokenはキャッシュされる? doorkeeperでプロテクトされているコントローラーのテスト