Spring Bootでmockitoを使ってテストする

Spring Bootでmockitoを使ってテストする

Spring Bootのgradle.buildで以下があればJUnit,mockito,assertJが使えます。

Spring BootのRESTControllerをJUnit4でテストする」にも書きましたが、サービスクラスで使用しているリポジトリクラスをモックするテスト方法を書いてみます。

依存関係にあるクラスをモックする

依存関係にあるクラスのみモックすることが出来ます。例えば、「コントローラ → サービス → リポジトリ」というような構成の場合、コントローラのテストでリポジトリをモックすることは出来ません。コントローラでモック出来るのはサービスクラスとなります。サービスクラスでモック出来るのはリポジトリインタフェースとなります。

以下のようなイメージです。

サービスクラスのテストでリポジトリインタフェースをモックする

サービスクラスのメソッド内でリポジトリクラス経由でDBにアクセスしています。

DemoServiceTest.java

サービスクラスがテスト対象なので@InjectMocksをつけます。

サービスクラスのメソッドで使用するリポジトリ(依存関係にある)に対して@Mockをつけます。@Mockをつけるのはサービスクラスでモックしたいクラスのみです。

@BeforeでMockitoAnnotations.initMocks(this);をしておかないと正常にモックが動作しません。

テストメソッドで、whenの引数にメソッドを指定します。thenThrow(new RuntimeException());でランタイムエクセプションをスローさせます。これでcatch句に遷移させることができます。スローさせずに値を返したい場合はthenReturnメソッドを使用します。

以下はサービスクラスです。

when().thenThrow()で注意

findAll()のような引数のないメソッドはthenThrow()されますが、自作のメソッドで引数が存在する場合はany()などを使用する必要があります。

例えば

というようにすると正常に動作しません。この場合、String型の引数なので以下のように記述します。

というようにanyString()を使用します。(anyStringはnullを含みません)

引数がオブジェクトの場合はany()を使用します。

上記をstaticインポートすればOKです。

@RunWith(MockitoJUnitRunner.class)アノテーションでモックする

JUnit4までは、@RunWith(MockitoJUnitRunner.class)をクラスにつけることによってモックすることも可能です。

@Beforeの部分は不要になります。

when~thenThrowが使えない場合がある

when~thenThrowでスローさせようとしていたのですが、どうも戻り値がvoidのメソッドに対してはwhen~thenThrowが使えないようです。「型 Mockito のメソッド when(T) は引数 (void) に適用できません」とエラーになると思います。

代わりにdoThrow~when、doReturn~when、doAnswer~when、doNothing~whenを使います。

doNothing~when,doReturn~when,doThrow~when

doNothing,doReturn,doThrowの使い方は以下の通りです。

メソッド ユースケース
doNothing 戻り値voidのメソッドで使用
doReturn 戻り値void以外のメソッドで使用
doThrow 例外発生させるテストで使用

whenの引数はインスタンスでそのメソッドをwhenの中ではなく、外でチェーンします。

これで戻り値がvoid型のメソッドもスローが出来るようになります。when~thenThrowより全てのテストケースでdoThrow~whenを使った方が良い気がします。

assertはassertThatExceptionOfTypeを使用します。

これでuploadメソッドが実行したときにXXExceptionが発生すればテストOKとなります。

mockitoではプライベートメソッドをモックすることができない

JMockitoならプライベートメソッドをモックすることができますが、mockitoではプライベートメソッドをモックすることができません。

mockitoのポリシーでprivateメソッドを使用する場合はコード設計がイケてない、という考えのようです。

org.mockito.mockとorg.mockito.spyの違い

org.mockito.Mockito()メソッドを使えばクラスのインスタンスをモックすることが出来ます。

PrivateKeyのモックです。RSAPrivateKey.classはPrivateKeyインタフェースをインプリメントした具象クラスです。

上記でモックすることが出来ますがmockで生成したインスタンスは偽物です。

これに対してspyで生成したインスタンスはメソッドは実際に動作します。

mockitoでプライベートメソッドをリフレクションでテストする

mockitoではプライベートメソッドをモックすることができませんが、プライベートメソッドのテストをすることは可能です。

以下のようにgetDeclaredMethodを使用します。

setAccessible(true)にしておきます。これでプライベートメソッド(上記ではcalcメソッド)のテストができます。

mockitoでコーディングする際、以下はstatic importしておくとよいです。

メソッドが呼ばれた回数を確認する

verifyでモックしたメソッドが呼ばれた回数を確認するテストをすることができます。

戻り値がないメソッドはverifyでテストすればよいと思います。以下のように書きます。

上記は、findByPk()メソッドが2回呼ばれていることを確認します。

サンプルテストコードです。

Please remove unnecessary stubbings or use ‘lenient’ strictness. More info: javadoc for UnnecessaryStubbingException class.

このエラーが出たら、使ってもいないインスタンスをモックしているというエラーです。

簡単に解決するには、@RunWith(Mockito.JUnitRunner.classを以下のように変更します。

これでエラーがでなくなります。

JUnit5 + mockito3.x

build.gradleで依存関係を追加します。

testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
testImplementation 'org.mockito:mockito-core:3.5.10'
testImplementation 'org.mockito:mockito-junit-jupiter:3.5.10'

view raw
build.gradle
hosted with ❤ by GitHub

@ExtendWith(MockitoExtension.class)をクラスレベルのアノテーションとして付与します。これを指定しないとモックできません。

JUnit5で「Please remove unnecessary stubbings or use ‘lenient’ strictness. More info: javadoc for UnnecessaryStubbingException class.」エラーが発生した場合は、クラスレベルのアノテーションに@MockitoSettings(strictness = Strictness.LENIENT)を付与すれば回避することが出来ます。(JUnit4では試していません)

mockito3.5.10で確認しましたが、MockitoAnnotations.initMocksメソッドは非推奨になっているようです。

@TestInstance(Lifecycle.PER_METHOD)をクラスレベルのアノテーションで付与します。

省略した場合、デフォルトはLifecycle.PER_METHODです。

@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // @Orderアノテーションででテスト順序を指定したい場合
@ExtendWith(MockitoExtension.class) // JUnit+Mockitoの場合
@MockitoSettings(strictness = Strictness.LENIENT)
@TestInstance(Lifecycle.PER_METHOD) // デフォルトはPER_METHOD
class SampleTest {
@InjectMocks private DemoService service;
@Mock private DemoRepository Repository;
@BeforeAll
static void initAll() {}
@BeforeEach
void init() {}
@Test
@Order(1) // 1番目に実行する
@DisplayName("更新APIサービスのupdateメソッドテスト")
void updateTest() {
//
}
@Test
@Order(2) // 2番目に実行する
@DisplayName("取得APIサービスのselectメソッドテスト")
void updateTest() {
//
}

view raw
SampleTest.java
hosted with ❤ by GitHub

HttpServletRequestをモックする

spring-test-x.x.x.RELEASE.jarにMockHttpServletRequestクラスが用意されているので、このクラスでモックします。

MockHttpServletRequest req = new MockHttpServletRequest();

view raw
Test.java
hosted with ❤ by GitHub

S3ObjectクラスのgetObjectContentメソッドをモックする

S3のgetObjectメソッドをモックするにはS3ObjectクラスgetObjectContentメソッドをモックする必要があります。

文字列をbyte配列に変換します。

S3ObjectInputStreamのインスタンスをsetObjectContentでセットしてモックすることができます。

getObjectメソッドの引数がGetObjectRequestインスタンスの場合は引数は一つに変更します。

Value Objectをモックする

getterがあってsetterがないVOをモックします。mockitoでモックする方法もありますが以下のようにgetterをoverrideして簡単にモックすることが出来ます。

http://www.mockobjects.com/2007/04/test-smell-everything-is-mocked.html

LocalDateTime.now()をモックする

mockitoとPowermockを併用すればモックすることができるようです。※未検証

抽象クラスのメソッドのテスト

抽象クラスは@InjectMockアノテーションを付与できません。

AbstractServiceのメソッドをテストする場合はMockito.mock(AbstractService.class, Mockito.CALLS_REAL_METHODS);として各メソッドのテストをすることができます。

APIのリクエストボディ

APIのテストでリクエストボディをMap<String, Object>型で作成します。

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA