Comment simuler un test d'API de repos à distance avec Spring?

supposons que j'ai fait un client simple dans mon application qui utilise un service web distant qui expose une API RESTful à un certain URI /foo/bar/{baz} . Maintenant, je souhaite tester mon client qui fait des appels à ce service web.

idéalement, dans mes tests, j'aimerais me moquer des réponses que j'obtiens du service web, étant donné une demande spécifique comme /foo/bar/123 ou /foo/bar/42 . Mon client suppose que L'API est en fait en cours d'exécution quelque part, donc j'ai besoin d'un "service web" local pour commencer Je cours sur http://localhost:9090/foo/bar pour mes tests.

je veux que les tests de mon unité soient indépendants, semblables aux tests des contrôleurs à ressort avec le cadre D'essai MVC à ressort.

un pseudo-code pour un client simple, récupérant des nombres à partir de l'API distante:

// Initialization logic involving setting up mocking of remote API at 
// http://localhost:9090/foo/bar

@Autowired
NumberClient numberClient // calls the API at http://localhost:9090/foo/bar

@Test
public void getNumber42() {
    onRequest(mockAPI.get("/foo/bar/42")).thenRespond("{ "number" : 42 }");
    assertEquals(42, numberClient.getNumber(42));
}

// ..

quelles sont mes alternatives avec Spring?

21
demandé sur Tuno 2014-08-29 12:32:21

7 réponses

si vous utilisez Spring RestTemplate vous pouvez utiliser MockRestServiceServer. Un exemple peut être trouvé ici https://objectpartners.com/2013/01/09/rest-client-testing-with-mockrestserviceserver/

9
répondu Tuno 2015-08-11 08:27:47

si vous voulez unité tester votre client, alors vous vous moqueriez des services qui font le reste des appels API, i.e. avec mockito - je suppose que vous avez un service qui fait ces appels API pour vous, non?

Si vous voulez "se moquer de" l'Api rest en ce qu'il est une sorte de serveur de vous donner des réponses, ce qui serait plus en phase de tests d'intégration, vous pouvez essayer l'un des nombreux cadre là-bas comme restituto , rest-driver ou betamax .

7
répondu theadam 2017-11-27 17:37:19

la meilleure méthode est d'utiliser WireMock . Ajouter les dépendances suivantes:

    <dependency>
        <groupId>com.github.tomakehurst</groupId>
        <artifactId>wiremock</artifactId>
        <version>2.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.igniterealtime.smack</groupId>
        <artifactId>smack-core</artifactId>
        <version>4.0.6</version>
    </dependency>

définir et utiliser le wiremock comme indiqué ci-dessous

@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);

String response ="Hello world";
StubMapping responseValid = stubFor(get(urlEqualTo(url)).withHeader("Content-Type", equalTo("application/json"))
            .willReturn(aResponse().withStatus(200)
                    .withHeader("Content-Type", "application/json").withBody(response)));
6
répondu Yallaling Goudar 2018-07-26 22:43:02

ce que vous recherchez est la prise en charge de Client-side REST Tests dans le cadre de test MVC printemps.

en supposant que votre NumberClient utilise le RestTemplate de Spring, ce support mentionné ci-dessus est la voie à suivre!

Espère que cette aide,

Sam

1
répondu Sam Brannen 2016-11-15 03:20:03

vous pouvez facilement utiliser Mockito pour simuler une API REST dans Spring Boot .

mettez un régulateur à éclats dans votre arbre d'essai:

@RestController
public class OtherApiHooks {

    @PostMapping("/v1/something/{myUUID}")
    public ResponseEntity<Void> handlePost(@PathVariable("myUUID") UUID myUUID ) {
        assert (false); // this function is meant to be mocked, not called
        return new ResponseEntity<Void>(HttpStatus.NOT_IMPLEMENTED);
    }
}

votre client devra appeler L'API sur localhost lors de l'exécution des tests. Cela pourrait être configuré dans src/test/resources/application.properties . Si le test utilise RANDOM_PORT , votre client devra trouver cette valeur. C'est un peu difficile, mais le problème est abordé ici: Boot ressort - Comment obtenir le port courant

configurer votre classe de test pour utiliser un WebEnvironment (un serveur en cours d'exécution) et maintenant votre test peut utiliser Mockito de la manière standard, en retournant ResponseEntity objets selon les besoins:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestsWithMockedRestDependencies {

  @MockBean private OtherApiHooks otherApiHooks;

  @Test public void test1() {
    Mockito.doReturn(new ResponseEntity<Void>(HttpStatus.ACCEPTED))
      .when(otherApiHooks).handlePost(any());
    clientFunctionUnderTest(UUID.randomUUID()); // calls REST API internally
    Mockito.verify(otherApiHooks).handlePost(eq(id));
  }

}

vous pouvez également l'utiliser pour tester de bout en bout l'ensemble de votre microservice dans un environnement avec la maquette créée ci-dessus. Une façon de le faire est de injectez TestRestTemplate dans votre classe test, et utilisez-le pour appeler votre REST API à la place de clientFunctionUnderTest de l'exemple.

@Autowired private TestRestTemplate restTemplate;
@LocalServerPort private int localPort; // you're gonna need this too

comment cela fonctionne

parce que OtherApiHooks est un @RestController dans l'arbre d'essai, La Botte à ressort établira automatiquement le service de repos spécifié lors de l'exécution du SpringBootTest.WebEnvironment .

Mockito est utilisé ici pour se moquer de la contrôleur de classe, pas le service dans son ensemble. Par conséquent, il y aura un certain traitement côté serveur géré par le démarrage du ressort avant que la simulation ne soit lancée. Cela peut inclure des choses telles que la deserialisation (et la validation) du chemin UUID montré dans l'exemple.

de ce que je peux dire, cette approche est robuste pour les essais en parallèle avec IntelliJ et Maven.

1
répondu nobar 2018-03-30 00:47:25

si vous utilisez le repos et l'optimisation du DTO pattern , alors je vous recommande de suivre ce tutoriel .

0
répondu Manu 2014-08-29 14:19:12

voici un exemple de base sur la façon de simuler une classe de contrôleur avec Mockito :

la classe du contrôleur:

@RestController
@RequestMapping("/users")
public class UsersController {

    @Autowired
    private UserService userService;

    public Page<UserCollectionItemDto> getUsers(Pageable pageable) {
        Page<UserProfile> page = userService.getAllUsers(pageable);
        List<UserCollectionItemDto> items = mapper.asUserCollectionItems(page.getContent());
        return new PageImpl<UserCollectionItemDto>(items, pageable, page.getTotalElements());
    }
}

configurer les haricots:

@Configuration
public class UserConfig {

    @Bean
    public UsersController usersController() {
        return new UsersController();
    }

    @Bean
    public UserService userService() {
        return Mockito.mock(UserService.class);
    }
}

le UserCollectionItemDto est un simple POJO et il représente ce que le consommateur API envoie au serveur. Le profile utilisateur est l'objet principal utilisé dans la couche service (par la classe UserService ). Ce comportement met également en œuvre le DTO pattern .

Enfin, maquette le comportement attendu:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@Import(UserConfig.class)
public class UsersControllerTest {

    @Autowired
    private UsersController usersController;

    @Autowired
    private UserService userService;

    @Test
    public void getAllUsers() {
        initGetAllUsersRules();
        PageRequest pageable = new PageRequest(0, 10);
        Page<UserDto> page = usersController.getUsers(pageable);
        assertTrue(page.getNumberOfElements() == 1);
    }

    private void initGetAllUsersRules() {
        Page<UserProfile> page = initPage();
        when(userService.getAllUsers(any(Pageable.class))).thenReturn(page);
    }

    private Page<UserProfile> initPage() {
        PageRequest pageRequest = new PageRequest(0, 10);
        PageImpl<UserProfile> page = new PageImpl<>(getUsersList(), pageRequest, 1);
        return page;
    }

    private List<UserProfile> getUsersList() {
        UserProfile userProfile = new UserProfile();
        List<UserProfile> userProfiles = new ArrayList<>();
        userProfiles.add(userProfile);
        return userProfiles;
    }
}

l'idée est d'utiliser le pur bean Controller et de maquiller ses membres. Dans cet exemple, nous nous sommes moqués de l'objet UserService.getUsers() pour contenir un utilisateur et nous avons ensuite validé si le contrôleur retournerait le bon nombre d'utilisateurs.

Avec la même logique, vous pouvez tester le Service et d'autres niveaux de votre application. Cet exemple utilise le contrôleur-Service-Dépôt ainsi que:)

0
répondu Menelaos Kotsollaris 2017-08-07 18:53:30