web-dev-qa-db-fra.com

Comment se moquer de la télécommande REST API en test unitaire avec Spring?

Supposons que, dans mon application, un client simple utilise un service Web distant qui expose une API RESTful à un URI /foo/bar/{baz}. Maintenant, je souhaite tester un peu mon client qui passe des appels vers ce service Web.

Idéalement, lors de mes tests, je voudrais me moquer des réponses que je reçois du service Web, à partir d’une requête spécifique telle que /foo/bar/123 ou /foo/bar/42. Mon client suppose que l'API est en cours d'exécution quelque part. J'ai donc besoin d'un "service Web" local pour pouvoir s'exécuter sur http://localhost:9090/foo/bar pour mes tests.

Je souhaite que mes tests unitaires soient autonomes, similaires aux tests des contrôleurs Spring avec la structure de test Spring MVC. 

Quelques pseudo-codes pour un client simple, récupérant des numéros à 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?

22
user1019830

Si vous utilisez Spring RestTemplate, vous pouvez utiliser MockRestServiceServer. Vous trouverez un exemple ici https://objectpartners.com/2013/01/09/rest-client-testing-with-mockrestserviceserver/

12
Tuno

La meilleure méthode consiste à utiliser WireMock . Ajoutez 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éfinissez et utilisez le câble 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)));
8
Yallaling Goudar

Si vous voulez unit tester votre client, vous simulerez les services qui effectuent les appels d'API REST, c'est-à-dire avec mockito -. Je suppose que vous disposez d'un service faire ces appels API pour vous, non?

Si par contre vous voulez "simuler" les API restantes dans la mesure où il existe une sorte de serveur qui vous donne des réponses, ce qui serait plus en ligne avec les tests d'intégration, vous pouvez essayer l'un des nombreux frameworks existants comme restito , rest-driver ou betamax

7
theadam

Ce que vous recherchez, c'est le support pour Tests REST côté client dans Spring MVC Test Framework.

En supposant que votre NumberClient utilise la RestTemplate de Spring, cet appui susmentionné est la voie à suivre!

J'espère que cela t'aides,

Sam

1
Sam Brannen

Si vous utilisez rest et utilisez le modèle DTO , je vous recommande de suivre ce tutoriel

0
Manu

Voici un exemple de base sur la façon de se moquer d’une classe de contrôleurs avec Mockito :

La classe de 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());
    }
}

Configurez les haricots:

@Configuration
public class UserConfig {

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

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

UserCollectionItemDto est un simple POJO et représente ce que le consommateur d'API envoie au serveur. UserProfile est l'objet principal utilisé dans la couche de service (par la classe UserService). Ce comportement implémente également le modèle DTO .

Enfin, simulez 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 bean Controller pur et de simuler ses membres. Dans cet exemple, nous avons simulé l'objet UserService.getUsers() pour contenir un utilisateur, puis nous avons vérifié si le contrôleur renverrait le nombre correct d'utilisateurs.

Avec la même logique, vous pouvez tester le service et les autres niveaux de votre application. Cet exemple utilise également le modèle Controller-Service-Repository Pattern :)

0