Android-eenheidstest met Retrofit2 en Mockito of Robolectric

Kan ik de echte respons van retrofit2beta4 testen? Heb ik Mockito of Robolectica nodig?

Ik heb geen activiteiten in mijn project, het wordt een bibliotheek en ik moet testen of de server correct reageert.
Nu heb ik zo’n code en zit vast…

@Mock
ApiManager apiManager;
@Captor
private ArgumentCaptor<ApiCallback<Void>> cb;
@Before
public void setUp() throws Exception {
    apiManager = ApiManager.getInstance();
    MockitoAnnotations.initMocks(this);
}
@Test
public void test_login() {
    Mockito.verify(apiManager)
           .loginUser(Mockito.eq(login), Mockito.eq(pass), cb.capture());
    // cb.getValue();
    // assertEquals(cb.getValue().isError(), false);
}

Ik kan een nepantwoord geven, maar ik moet het echt testen. Is het succes? Is het lichaam correct?
Kun je me helpen met code?


Antwoord 1, autoriteit 100%

Het is over het algemeen geen goed idee om echte serververzoeken te testen. Zie deze blogpostvoor een interessante discussie over het onderwerp. Volgens de auteur is het gebruik van je echte server een probleem omdat:

  • Nog een bewegend stuk dat af en toe kan falen
  • Vereist enige expertise buiten het Android-domein om de server te implementeren en up-to-date te houden
  • Moeilijk om fout-/randgevallen te activeren
  • Langzame testuitvoering (maakt nog steeds HTTP-aanroepen)

Je kunt alle bovenstaande problemen vermijden door een nepserver te gebruiken, zoals OkHttp’s MockWebServerom echte responsresultaten te simuleren. Bijvoorbeeld:

@Test
public void test() throws IOException {
    MockWebServer mockWebServer = new MockWebServer();
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(mockWebServer.url("").toString())
            //TODO Add your Retrofit parameters here
            .build();
    //Set a response for retrofit to handle. You can copy a sample
    //response from your server to simulate a correct result or an error.
    //MockResponse can also be customized with different parameters
    //to match your test needs
    mockWebServer.enqueue(new MockResponse().setBody("your json body"));
    YourRetrofitService service = retrofit.create(YourRetrofitService.class);
    //With your service created you can now call its method that should 
    //consume the MockResponse above. You can then use the desired
    //assertion to check if the result is as expected. For example:
    Call<YourObject> call = service.getYourObject();
    assertTrue(call.execute() != null);
    //Finish web server
    mockWebServer.shutdown();
}

Als u netwerkvertragingen moet simuleren, kunt u uw reactie als volgt aanpassen:

MockResponse response = new MockResponse()
    .addHeader("Content-Type", "application/json; charset=utf-8")
    .addHeader("Cache-Control", "no-cache")
    .setBody("{}");
response.throttleBody(1024, 1, TimeUnit.SECONDS);

Als alternatief kunt u MockRetrofiten NetworkBehaviorgebruiken om API-reacties te simuleren. Zie hiereen voorbeeld van hoe het te gebruiken.

Ten slotte, als u uw Retrofit-service alleen wilt testen, is het het gemakkelijkst om er een nepversie van te maken die nepresultaten voor uw tests afgeeft. Als u bijvoorbeeld de volgende GitHub-service-interface heeft:

public interface GitHub {
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(
        @Path("owner") String owner,
        @Path("repo") String repo);
}

Je kunt dan de volgende MockGitHubmaken voor je tests:

public class MockGitHub implements GitHub {
    private final BehaviorDelegate<GitHub> delegate;
    private final Map<String, Map<String, List<Contributor>>> ownerRepoContributors;
    public MockGitHub(BehaviorDelegate<GitHub> delegate) {
        this.delegate = delegate;
        ownerRepoContributors = new LinkedHashMap<>();
        // Seed some mock data.
        addContributor("square", "retrofit", "John Doe", 12);
        addContributor("square", "retrofit", "Bob Smith", 2);
        addContributor("square", "retrofit", "Big Bird", 40);
        addContributor("square", "picasso", "Proposition Joe", 39);
        addContributor("square", "picasso", "Keiser Soze", 152);
    }
    @Override public Call<List<Contributor>> contributors(String owner, String repo) {
        List<Contributor> response = Collections.emptyList();
        Map<String, List<Contributor>> repoContributors = ownerRepoContributors.get(owner);
        if (repoContributors != null) {
            List<Contributor> contributors = repoContributors.get(repo);
            if (contributors != null) {
                response = contributors;
            }
        }
        return delegate.returningResponse(response).contributors(owner, repo);
    }
}

U kunt dan de MockGitHubgebruiken voor uw tests om het soort reacties te simuleren waarnaar u op zoek bent. Zie voor het volledige voorbeeld de implementaties van de SimpleServiceen SimpleMockServicevoor deze Retrofit-voorbeeld.

Dit alles gezegd hebbende, als u absoluut verbinding moet maken met de eigenlijke server, kunt u Retrofit zo instellen dat het synchroon werkt met een aangepaste ImmediateExecutor:

public class ImmediateExecutor implements Executor {
    @Override public void execute(Runnable command) {
        command.run();
    }
}

Pas het vervolgens toe op de OkHttpClientdie u gebruikt bij het bouwen van de Retrofit:

OkHttpClient client = OkHttpClient.Builder()
        .dispatcher(new Dispatcher(new ImmediateExecutor()))
        .build();
Retrofit retrofit = new Retrofit.Builder()
        .client(client)
        //Your params
        .build();

Antwoord 2, autoriteit 16%

Het antwoord is te makkelijk dan ik had verwacht:

Als u CountDownLatch gebruikt, wacht uw test totdat u countDown() aanroept

public class SimpleRetrofitTest {
private static final String login = "your@login";
private static final String pass = "pass";
private final CountDownLatch latch = new CountDownLatch(1);
private ApiManager apiManager;
private OAuthToken oAuthToken;
@Before
public void beforeTest() {
    apiManager = ApiManager.getInstance();
}
@Test
public void test_login() throws InterruptedException {
    Assert.assertNotNull(apiManager);
    apiManager.loginUser(login, pass, new ApiCallback<OAuthToken>() {
        @Override
        public void onSuccess(OAuthToken token) {
            oAuthToken = token;
            latch.countDown();
        }
        @Override
        public void onFailure(@ResultCode.Code int errorCode, String errorMessage) {
            latch.countDown();
        }
    });
    latch.await();
    Assert.assertNotNull(oAuthToken);
}
@After
public void afterTest() {
    oAuthToken = null;
}}

Antwoord 3

Tenzij u de QA-server-API test, is het om verschillende redenen een slecht idee.

  • Ten eerste vult u uw productiedatabase met slecht/nep
    gegevens
  • Serverbronnen gebruiken, wanneer ze beter kunnen worden gebruikt om te dienen
    geldig verzoek

De beste manier om Mockito te gebruiken, of uw reacties te bespotten

Als u uw productie-API moet testen, test deze dan een keer en voeg @Ignore-annotatie toe. Op die manier worden ze niet de hele tijd uitgevoerd en spammen ze uw server niet met nepgegevens en kunt u deze gebruiken wanneer u denkt dat de api zich niet correct gedraagt.

Other episodes