Mockito の使用方法
Android Studio で Mockito を使用してテストする方法を記載します。
ここでは、以下のBookクラスをテストする方法を記載します。
普通にテストしようとすると new やstaticメソッドがあり、一見すると不可能なぐらい大変です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Book { // テスト対象クラス static Book newBook() { Book book = new Book(); Page page = new Page(); page.setListener(book.mPageCb); return book; } private Consumer<Integer> mPageCb = integer -> { mContents = "Page=" + integer + " style=" + Style.getStyle(); }; public String mContents = ""; } class Page { // ライブラリ private Consumer<Integer> mListener; void setListener(Consumer<Integer> listener) { mListener = listener; } } class Style { // ライブラリ public static int getStyle() { return 1; } } |
さて、一見、難題に見えますが、Mockitoを使用すると比較的簡単にテストすることができます。
まずは、build.gradleに以下を追加します。
1 2 |
testImplementation "org.mockito:mockito-core:3.5.15" testImplementation "org.mockito:mockito-inline:3.5.15" |
次に最も簡単なテストを書いてみます。
1 2 3 4 5 6 7 |
public class ExampleUnitTest { @Test public void Book_01() { Book book = Book.newBook(); assertEquals("", book.mContents); } } |
さて、ここまではOKだと思います。この次、一体どうすれば良いのか。
最初にPageクラスをモック化する必要があります。
しかし、PageクラスはBookクラス内部で生成されています。そこで、newをモック化(フック)してPageクラスをMock化したインスタンスを返すことにします。
また、Mock化したPageクラスを取得することができたので、続いて、Mock化されたインスンタンスのsetListenerを書き換えて、引数に設定されたListenerを保持しておきます。
これにより、自由にListenerを呼び出して試験することができます。
これで、99を設定して呼び出すことで、「page=99 style=1」という出力を試験することができました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Page mMockPage; Consumer<Integer> mPassedListener; @Test public void Book_02() { // Page.classのnewをhookする MockedConstruction mockedConstruction = Mockito.mockConstruction(Page.class, (mock, context) -> { // mockには pageのMock化されたクラスが渡されてくるので、これを保存しておく mMockPage = mock; // Mock化されたインスンタンスのsetListenerを書き換えて、引数に設定されたListenerを保持しておく Mockito.doAnswer(invocation -> { Object[] args = invocation.getArguments(); mPassedListener = (Consumer<Integer>)args[0]; return null; }).when(mMockPage).setListener(Mockito.any()); }); Book book = Book.newBook(); mPassedListener.accept(99); // コールバックのインスタンスを手に入れたので自由に呼び出せる assertEquals("page=99 style=1", book.mContents); mockedConstruction.close(); // newやstaticをHookした場合はcloseして置かないと隣のテストで重複してopenして失敗する。 } |
ここまでくれば、あとひとつ、styleのstaticメソッドもモック化(フック)してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
int mReturnStyle; @Test public void Book_03() { // Page.classのnewをhookする MockedConstruction mockedConstruction = Mockito.mockConstruction(Page.class, (mock, context) -> { // mockには pageのMock化されたクラスが渡されてくるので、これを保存しておく mMockPage = mock; // Mock化されたインスンタンスのsetListenerを書き換えて、引数に設定されたListenerを保持しておく Mockito.doAnswer(invocation -> { Object[] args = invocation.getArguments(); mPassedListener = (Consumer<Integer>)args[0]; return null; }).when(mMockPage).setListener(Mockito.any()); }); MockedStatic mockedStatic = Mockito.mockStatic(Style.class); Mockito.when(Style.getStyle()).thenAnswer(invocation -> { return mReturnStyle; }); Book book = Book.newBook(); mReturnStyle = 48; // 自由に戻り値を変更できる mPassedListener.accept(99); // コールバックのインスタンスを手に入れたので自由に呼び出せる assertEquals("page=99 style=48", book.mContents); mockedConstruction.close(); // newやstaticをHookした場合はcloseして置かないと隣のテストで重複してopenして失敗する。 mockedStatic.close(); } |
上記コードでは、Mockito.mockStaticを使用して、getStyle()関数を自由に書き換えて試験しています。
1 2 3 4 |
まとめ: ・Mockitoを使用して new をフック(モック化) ・Mockitoを使用してモック化したオブジェクトにを自由に書き換えてた ・Mockitoを使用してstatic関数をモック化 |
コメントを残す