2016-03-02 4 views
42

Puis-je tester une réelle réponse de retrofit2beta4? Ai-je besoin de Mockito ou de Robolectic?Test d'unité Android avec Retrofit2 et Mockito ou Robolectric

Je n'ai pas d'activités dans mon projet, ce sera une bibliothèque et j'ai besoin de tester si le serveur répond correctement. Maintenant, j'ai un tel code et coincé ...

@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); 
} 

je peux répondre par de faux, mais je dois tester réel. Est-ce le succès? Est-ce le corps correct? Pouvez-vous m'aider avec le code?

Répondre

15

La réponse est trop facile que je pensais:

L'utilisation CountDownLatch rend votre t is attendre jusqu'à ce que vous appelez Countdown()

public class SimpleRetrofitTest { 

private static final String login = "[email protected]"; 
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; 
}} 
85

Ce n'est généralement pas une bonne idée de tester les demandes réelles du serveur. Voir this blog post pour une discussion intéressante sur le sujet. Selon l'auteur, en utilisant votre serveur réel est un problème parce que:

  • Une autre pièce mobile qui peut échouer par intermittence
  • nécessite une certaine expertise en dehors du domaine Android pour déployer le serveur et le tenir à jour
  • difficiles à déclencher des cas d'erreur/bord
  • exécution d'un test lent (toujours faire des appels HTTP)

Vous pouvez éviter tous les problèmes ci-dessus en utilisant un serveur fictif tel que MockWebServer d'OkHttp pour simuler des résultats de réponse réels. Par exemple:

@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(); 
} 

Si vous avez besoin de simuler des retards de réseau, vous pouvez personnaliser votre réponse comme suit:

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

Vous pouvez également utiliser MockRetrofit et NetworkBehavior pour simuler les réponses de l'API. Voir here un exemple de la façon de l'utiliser. Enfin, si vous voulez simplement tester votre service Retrofit, le plus simple serait de créer une version simulée qui émet des résultats fictifs pour vos tests. Par exemple, si vous avez la GitHub interface de service suivante:

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

Vous pouvez ensuite créer le MockGitHub suivant pour vos 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); 
    } 
} 

Vous pouvez ensuite utiliser le MockGitHub sur vos tests simuler les types des réponses que vous recherchez. Pour l'exemple complet, voir les implémentations de SimpleService et SimpleMockService pour ce Retrofit example.

Ayant dit tout cela, si vous devez absolument vous connecter au serveur réel, vous pouvez définir Retrofit pour travailler de manière synchrone avec une coutume ImmediateExecutor:

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

appliquent ensuite aux OkHttpClient que vous utilisez lors de la construction du Retrofit :

OkHttpClient client = OkHttpClient.Builder() 
     .dispatcher(new Dispatcher(new ImmediateExecutor())) 
     .build(); 

Retrofit retrofit = new Retrofit.Builder() 
     .client(client) 
     //Your params 
     .build(); 
+0

Toute votre réponse est basée sur la "simulation". Comme je l'ai dit sur le sujet de la question - je peux le faire. Mon projet est une bibliothèque qui fonctionnera avec l'API du serveur. La seule chose que j'ai besoin de tester - change sur le serveur, j'ai besoin de tester de vraies réponses. – AndrewS

+2

J'ai proposé des alternatives parce que je pense que cela n'a pas de sens de tester sur un vrai serveur. Vous ne pouvez pas être sûr que le test fonctionnera dans différents endroits pour différents utilisateurs, vous ne pouvez pas facilement tester les problèmes de connexion, etc. Le serveur n'est pas quelque chose qui appartient à votre bibliothèque et ne devrait pas être traité comme tel, je pense. C'est pourquoi il est généralement préférable de tester le serveur _responses_ à la place. Si vous utilisez un 'MockWebServer', vous pouvez faire exécuter vos tests comme s'ils étaient connectés au vrai serveur. Votre bibliothèque ne connaitrait pas la différence. –

+2

Si je fais la réponse de faux succès - j'obtiendrai le test de succès. Quel est le but de ce test? J'ai juste besoin de savoir quand la réponse du serveur a changé (par des tests), pour mettre à jour ma bibliothèque pour une nouvelle réponse. Je ne saurai jamais que quelque chose a changé si je fais une fausse réponse. – AndrewS

-2

À moins que vous testez API serveur QA, il est une mauvaise idée pour plusieurs raisons.

  • Tout d'abord vous alimenter votre base de données de production avec mauvais/faux données
  • L'utilisation des ressources du serveur, quand ils peuvent mieux utilisés pour servir demande valide

La meilleure façon d'utiliser Mockito, ou Mock your responses

De même, si vous devez tester votre API de production, testez-la une fois et ajoutez l'annotation @Ignore. De cette façon, ils ne sont pas exécutés tout le temps et ne spamment pas votre serveur avec de fausses données et vous pouvez l'utiliser quand vous pensez que l'API ne se comporte pas correctement.

+0

Que pensez-vous du moment où vous voulez vérifier si votre logique gère correctement la mise à jour constante des données du serveur et non les données périmées dont vous vous êtes moqué dans vos fichiers locaux? – miroslavign

+0

@miroslavign ne sais pas ce que vous entendez par 'vérifier si votre logique gère correctement la mise à jour constante des données du serveur'. Le client ne devrait pas être responsable de s'assurer que l'API fonctionne correctement. Et comment l'application spammer le serveur avec de fausses données? L'analytique disparaît des graphiques, car la plupart de ces tests unitaires sont exécutés dans le cadre de CI. Si vous vous inquiétez des données périmées, vous devez par exemple mettre à jour votre réponse bloquée – Akshay

+0

par exemple, je veux que chaque jour vérifie si l'analyse des données provenant du point de terminaison du serveur échoue -> signification, données du serveur json changées -> drapeau rouge - > Je devrais mettre à jour mes méthodes d'analyse syntaxique/POJO ou autre. – miroslavign