web-dev-qa-db-fra.com

Comment puis-je tester le code d'unité qui appelle l'API client Jersey?

J'ai écrit du code qui appelle l'API client Jersey, qui à son tour appelle un service Web qui est hors de mon contrôle. Je ne souhaite pas que mon test unitaire appelle le service Web actuel.

Quelle est la meilleure approche pour écrire un test unitaire pour le code qui appelle l’API client Jersey? Dois-je utiliser l'API de serveur Jersey pour écrire un service Web JAX-RS, puis utiliser Jersey Test Framework pour le test unitaire? Ou devrais-je me moquer des appels de service Web Jersey? J'ai accès à JMock. Ou devrais-je essayer une autre approche?

Au cours de mes recherches, j'ai trouvé cette discussion décrivant diverses options, mais j'ai trouvé une solution complète. Existe-t-il des exemples de code disponibles illustrant une approche JUnit suggérée? Je n’en ai trouvé aucune dans la documentation de Jersey.

Voici le code source pertinent:

public String getResult(URI uri) throws Exception {
  // error handling code removed for clarity
  ClientConfig clientConfig = new DefaultClientConfig();
  Client client = Client.create(clientConfig);
  WebResource service = client.resource(uri);
  String result = service.accept(accept).get(String.class);
  return result;
}

Voici des exemples de code de test que j'aimerais passer. Je voudrais tester (1) en passant un URI valide, en récupérant une chaîne valide et en (2) en transmettant un URI invalide (pour quelque raison que ce soit - inaccessible ou non autorisé) et en récupérant une exception.

@Test
public void testGetResult_ValidUri() throws Exception {
  String xml = retriever.getResult(VALID_URI);
  Assert.assertFalse(StringUtils.isBlank(xml));
}

@Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
  retriever.getResult(INVALID_URI);
}

Tout ce qui précède est la description simple de ce que fait mon code. En réalité, il y a une couche au-dessus de celle qui accepte deux URI, tente d'abord d'appeler le premier URI, et si cet URI échoue, elle essaie d'appeler le deuxième URI. Je voudrais que les tests unitaires couvrent (1) la première adresse URI réussie, (2) la première adresse URI échouant et la deuxième adresse URI réussie et (3) les deux adresses URI échouant. Ce code est suffisamment complexe pour que je veuille tester ces différents scénarios à l'aide de JUnit. Toutefois, pour ce faire, je dois soit exécuter des services Web de remplacement, soit simuler les appels de l'API client Jersey.

26
BennyMcBenBen

Essayez d’utiliser Mockito ou Easymock pour vous moquer des appels de service. Vous devez vous moquer uniquement des méthodes réellement utilisées - inutile de vous moquer de toutes les méthodes. Vous pouvez créer un objet fantaisie pour la classe WebResource, puis simuler un appel de méthode d'acceptation.

Dans la méthode de test @ BeforeClass/@ Before JUnit, écrivez quelque chose comme (exemple Mockito)

WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);

Ensuite, dans vos tests, vous pouvez utiliser res object comme s'il s'agissait d'un objet réel et appeler la méthode fictive dessus. Au lieu de renvoyer de la valeur, vous pouvez également générer des exceptions. Mockito est assez cool.

13
Piotr Kochański

En règle générale, ce que vous recherchez réellement est "la manière dont j’utilise le client Jersey de DSL génère une demande à l’URL correcte avec les paramètres de charge utile et d’URL corrects" . Tester ceci avec Mockito est vraiment prolixe et le code d'installation finira généralement par ressembler à ceci:

    when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
    when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
    when(testAuthentication.request(
            MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
    when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
    when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());

Et ceci est fondamentalement juste pour une seule requête REST. C'est trop verbeux, et au lieu de tester le résultat espéré, vous ne faites que reproduire les étapes que vous jugez correctes lors de l'utilisation de Jersey Client DSL.

Au lieu de ce qui précède, je voudrais me moquer d’un service simple. Pour cela, j’ai utilisé WireMock qui démarre un serveur Jetty et où je peux stub des choses comme "s’attendre à une requête sur cette URL, répondent avec ce message et vérifient que la charge utile est bien" .

Je sais que nous sommes sur un test d'intégration et qu'il est un peu plus lent que d'utiliser simplement Mockito, mais j'apprécie de tester le résultat réel et la lisibilité des tests bien plus dans ce cas.

La configuration d'un test client Jersey basé sur WireMock ressemble à ceci:

@Test
public void exactUrlOnly() {
    stubFor(get(urlEqualTo("/some/thing"))
            .willReturn(aResponse()
                .withHeader("Content-Type", "text/plain")
                .withBody("Hello world!")));

   assertThat(testClient.get("/some/thing").statusCode(), is(200));
   assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
13
vertti

Il suffit d'implémenter un service similaire et dans votre configuration de test d'unité, démarrez le service à l'aide de HttpServerFactory.

0
Chase