web-dev-qa-db-ja.com

Android RetrofitとMockitoを使用したユニットテスト

レトロフィットAPI呼び出しメソッドをアクティビティコードから分離し、これらのメソッドで単体テストを実行したいと思います。1つの例:インターフェイス:

public interface LoginService {
    @GET("/auth")
    public void basicLogin(Callback<AuthObject> response);
}

これは呼び出しを行うメソッドです。メインアクティビティでは、イベントバスによってオブジェクトを取得します。

public class AuthAPI {
    private Bus bus;
    LoginService loginService;

    public AuthAPI(String username, String password) {
        this.bus = BusProvider.getInstance().getBus();
        loginService = ServiceGenerator.createService(LoginService.class,
                CommonUtils.BASE_URL,
                username,
                password);
    }

    public void Login() {

        loginService.basicLogin(new Callback<AuthObject>() {
            @Override
            public void success(AuthObject authObject, Response response) {
                bus.post(authObject);
            }

            @Override
            public void failure(RetrofitError error) {
                AuthObject authObject = new AuthObject();
                authObject.setError(true);
                bus.post(authObject);
            }
        });
    }

}

そしてここでテスト

@RunWith(MockitoJUnitRunner.class)
public class AuthCallTest extends TestCase {

    AuthAPI authAPI;

    @Mock
    private LoginService mockApi;

    @Captor
    private ArgumentCaptor<Callback<AuthObject>> cb;

    @Before
    public void setUp() throws Exception {
        authAPI = new AuthAPI("username", "password");
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testLogin() throws Exception {

        Mockito.verify(mockApi).basicLogin((cb.capture()));

        AuthObject authObject = new AuthObject();
        cb.getValue().success(authObject, null);

        assertEquals(authObject.isError(), false);
    }
}

テストを開始すると、このエラーが発生します

Wanted but not invoked:
mockApi.basicLogin(<Capturing argument>);
-> at AuthCallTest.testLogin(AuthCallTest.Java:42)
Actually, there were zero interactions with this mock.

私が間違ったこと、これは私を夢中にさせています私は成功せずにこのガイドに従おうとしました: http://www.mdswanson.com/blog/2013/12/16/reliable-Android-http-testing-with -retrofit-and-mockito.html

誰か助けて :(

14
user4852580

セットアップ手順を見逃しているため、記事はあまり明確ではありません。記事にリンクされている GitHubプロジェクト にアクセスすると、不足している手順を説明する完全なソースコードを確認できます。

1)コードサンプルは、特定のアクティビティをテストするテストクラスから抽出されます。セットアップの一部として(つまり、_@Before_で)、GitHubAPI実装へのアクティビティの参照をモックのものに置き換えます。次に、アクティビティのonCreate()を呼び出します。

2)onCreate()の間に、アクティビティは現在置き換えられているGitHub APIを呼び出し、そのCallbackオブジェクトを渡します。

これらの最初の2つのステップは、各テストの開始時にMockito.verify(mockApi).repositories(Mockito.anyString(), cb.capture());ステップが機能する理由を説明しています。テストは_@Before_の後に実行されるため、mockApiは実際にそのrepositories()メソッドを呼び出しています。

コードの残りの部分は、それが配置されると理解しやすくなります。彼はmockApiを作成しただけで、実際に使用されているCallbackは変更していないため、アクティビティのコンテンツが変更されます。次に、コードの残りの部分は、ListViewまたはToastsをチェックすることにより、これらの変更が行われたことを確認します。


したがって、あなたの質問に答えるには、次のことを行う必要があります。

1)テストメソッドの開始時に、AuthAPIのloginServiceオブジェクトをmockApiオブジェクトに置き換えてから、AuthAPI.Login()を呼び出します。

2)関数が呼び出されたことを確認するために、verify()を使用します。

3)サンプルAuthObjectを作成し、それをcb.getValue().success()関数に渡します。

4)AuthObjectからBusを取得し、それがcallback.success()関数に送信したものと同じであることを表明します。

これは、AuthAPI.Login()がRetrofitから取得するBusAuthObjectに正しく送信することをテストします。


(SOの質問は少し前に書かれたものですが、最近同じ記事に出くわして同じ混乱があったので、この答えを思いました他の人にも役立つかもしれません。)

21
Steve Haley

問題は、間違ったタイミングでverifyを呼び出すことです。verifyの目的は、mockApiとの相互作用が期待どおりであることを確認することです。したがって、通常は次のように表示されます。

authApi.login();
Mockito.verify(mockApi).basicLogin((cb.capture()));

これは、エラーメッセージが示していることでもあります。verifybasicLoginが呼び出されることを期待していましたが、そうではありませんでした。

私もその記事を読んで、何か足りないものがあると感じました。私は実際にはまだ引数のキャプチャを理解していません。だからそれであなたを助けることはできません:)

1
user2143491