12
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Javaユニットテストの作り方(JUnit & Mockito & PowerMock)

Last updated at Posted at 2018-11-22

自分用のまとめです。
以下のライブラリを使ったJavaのユニットテストの作り方を記載します。

  • JUnit(テスティングフレームワーク)
  • Mockito(モック作成フレームワーク)
  • PowerMock(Mockitoの拡張フレームワーク)

Gradleの設定

必要なファイル(JUnit, Mockito, PowerMock)のダウンロードにはGradleを使いました。
設定ファイルの内容は以下の通りです。

build.gradle
apply plugin: 'java'

repositories {
    jcenter()
}

dependencies {
    testCompile 'junit:junit:4.12'
    testCompile "org.mockito:mockito-core:2.+"
    testCompile 'org.powermock:powermock-module-junit4:2.0.0-RC.4'
    testCompile 'org.powermock:powermock-api-mockito2:2.0.0-RC.4'
}

テスト対象クラス

テスト対象となるクラスです。
このクラスに対してJUnit, Mockito, PowerMockを使った場合を考えます。

Samole.java
public class Sample {
    private Sub sub;

    public Sample() {}

    public Sample(Sub sub) {
        this.sub = sub;
    }

    public String pubGet() {
        return "pubGet";
    }

    public String pubGet(String suffix) {
        return pubGet() + suffix;
    }

    public String pubGetSubValue() {
        return sub.getValue();
    }

    public String pubGetStaticValue() {
        return Static.getValue();
    }

    private String priGet() {
        return "priGet";
    }

    private String priGet(String suffix) {
        return priGet() + suffix;
    }

    public static class Sub {
        private String value;
        public Sub(String value) {
            this.value = value;
        }
        public String getValue() {
            return value;
        }
    }

    public static class Static {
        public static String getValue() {
            return "Static value";
        }
    }
}

JUnitのみのテストクラス

JUnitのみを使った場合です。

SampleTest.java
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;

public class SampleTest {
    private Sample instance;
    @Before
    public void init() {
        this.instance = new Sample();
    }
    @Test
    public void pubGet() {
        // publicメソッドのテスト
        String actual = instance.pubGet();
        assertThat(actual, is("pubGet"));
    }
    @Test
    public void priGet() throws Exception {
        // privateメソッドのテスト(引数なし)
        Method method = Sample.class.getDeclaredMethod("priGet");
        method.setAccessible(true);

        String actual = (String) method.invoke(instance);
        assertThat(actual, is("priGet"));
    }
    @Test
    public void priGet_withArg() throws Exception {
        // privateメソッドのテスト(引数あり)
        Method method = Sample.class.getDeclaredMethod("priGet", String.class);
        method.setAccessible(true);

        String actual = (String) method.invoke(instance, "Suffix");
        assertThat(actual, is("priGetSuffix"));
    }
    @Test
    public void pubGetSubValue() {
        Sample instance = new Sample(new Sample.Sub("test"));

        String actual = instance.pubGetSubValue();
        assertThat(actual, is("test"));
    }

}

JUnit & Mockitoを使ったテストクラス

Mockitoを追加した場合です。
モックを使ってメソッドの内容を書き換えることが出来ます。

SampleForMockitoTest.java
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class SampleForMockitoTest {
    @Mock
    private Sample mock;
    @Spy
    private Sample spy;
    @Before
    public void init() {
        // @Mockアノテーションのモックオブジェクトを初期化
        MockitoAnnotations.initMocks(this);
    }
    @Test
    public void pubGetSubValue() {
        // モックを使用しない場合
        Sample instance = new Sample(new Sample.Sub("test"));

        String actual = instance.pubGetSubValue();
        assertThat(actual, is("test"));
    }
    @Test
    public void pubGetSubValue_withMockito() {
        // モックを使用した場合は、スタブの値を返す
        Sample.Sub mock = mock(Sample.Sub.class);
        when(mock.getValue()).thenReturn("mock value");
        Sample instance = new Sample(mock);

        String actual = instance.pubGetSubValue();
        assertThat(actual, is("mock value"));
    }

    @Test
    public void pubGetSubValue_withMockitoMock() {
        // mock() はスタブを実装しないとデフォルト値(null)となる
        String actual1 = mock.pubGet();
        assertThat(actual1, is(nullValue()));

        // mock() はスタブを実装するとスタブの値を返す
        when(mock.pubGet()).thenReturn("mock value");
        String actual2 = mock.pubGet();
        assertThat(actual2, is("mock value"));
    }

    @Test
    public void pubGetSubValue_withMockitoSpy() {
        // spy() はスタブを実装しないと実際の値を返す
        String actual1 = spy.pubGet();
        assertThat(actual1, is("pubGet"));

        // spy() はスタブを実装するとスタブの値を返す
        when(spy.pubGet()).thenReturn("mock value");
        String actual2 = spy.pubGet();
        assertThat(actual2, is("mock value"));
    }
    @Test
    public void pubGetSubValue_withMockitoAnswer() {
        // mock() はスタブを実装するとスタブの値を返す
        // Answerを使うとスタブにメソッドの引数を利用することができる
        when(mock.pubGet(anyString())).thenAnswer(invocation -> {
            String arg1 = (String) invocation.getArguments()[0];
            return "mock value " + arg1;
        });
        String actual = mock.pubGet("suffix");
        assertThat(actual, is("mock value suffix"));
    }
}

JUnit & Mockito & PowerMockを使ったテストクラス

PowerMockを追加した場合です。
staticメソッドの内容を書き換えることが出来ます。

SamleForPowerMockTest.java
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Sample.Static.class})
public class SamleForPowerMockTest {
    private Sample instance;
    @Before
    public void init() {
        instance = new Sample();
    }
    @Test
    public void pubGetWithStatic() {
        // staticメソッドの値をテスト
        String actual = instance.pubGetStaticValue();
        assertThat(actual, is("Static value"));
    }
    @Test
    public void pubGetWithStatic_withPowerMock() {
        // mockStatic() でstaticメソッドがスタブの値を返す
        PowerMockito.mockStatic(Sample.Static.class);
        // 目的のメソッド以外は元々の挙動としたい場合は代わりに PowerMockito.spy() を使う
        // PowerMockito.spy(Sample.Static.class); 
        PowerMockito.when(Sample.Static.getValue()).thenReturn("PowerMockito value");

        String actual = instance.pubGetStaticValue();
        assertThat(actual, is("PowerMockito value"));
    }
}

まとめ

PowerMockを使うと強力な書き換えを行うことができる。
しかしPowerMockに頼りすぎると本来の目的(拡張性のある実装)を見失い、
カバレッジを稼ぐだけのテストになる危険性も感じたので使い所は考えた方が良さそう。

備考

MockitoとPowerMockの使い分けを参考にさせて頂きました。

12
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?