unitarias pruebas assured java unit-testing rest spring-test

java - pruebas - ¿Cómo simular la API REST remota en una prueba unitaria con Spring?



pruebas unitarias spring boot (7)

Aquí hay un ejemplo básico sobre cómo Mockito una clase de Controlador con Mockito :

La clase de controlador:

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

Configurar los frijoles:

@Configuration public class UserConfig { @Bean public UsersController usersController() { return new UsersController(); } @Bean public UserService userService() { return Mockito.mock(UserService.class); } }

El UserCollectionItemDto es un simple POJO y representa lo que el consumidor de API envía al servidor. El perfil de usuario es el objeto principal utilizado en la capa de servicio (por la clase UserService ). Este comportamiento también implementa el patrón DTO .

Finalmente, maquese el comportamiento esperado:

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

La idea es usar el bean Controller puro y maquetar a sus miembros. En este ejemplo, UserService.getUsers() objeto UserService.getUsers() para contener un usuario y luego validamos si el Controlador devolvería el número correcto de usuarios.

Con la misma lógica puede probar el Servicio y otros niveles de su aplicación. Este ejemplo también utiliza el patrón controlador-servicio-repositorio :)

Supongamos que he creado un cliente simple en mi aplicación que utiliza un servicio web remoto que está exponiendo una API RESTful en algún URI /foo/bar/{baz} . Ahora deseo realizar una prueba unitaria de mi cliente que realiza llamadas a este servicio web.

Idealmente, en mis pruebas, me gustaría burlarme de las respuestas que recibo del servicio web, dada una solicitud específica como /foo/bar/123 o /foo/bar/42 . Mi cliente asume que la API se está ejecutando en algún lugar, por lo que necesito un "servicio web" local para comenzar a ejecutarme en http://localhost:9090/foo/bar para mis pruebas.

Quiero que mis pruebas unitarias sean autónomas, similares a las pruebas de los controladores Spring con el marco de pruebas Spring MVC.

Algunos pseudocódigo para un cliente simple, obteniendo números de la API remota:

// 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)); } // ..

¿Cuáles son mis alternativas usando Spring?


El mejor método es utilizar WireMock . Agregue las siguientes dependencias:

<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>

Defina y use el cable como se muestra a continuación

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


Lo que está buscando es el soporte para las pruebas REST del lado del cliente en el marco de prueba Spring MVC.

Suponiendo que su NumberClient use Spring''s RestTemplate , este soporte mencionado es el camino a seguir.

Espero que esto ayude,

Sam


Puedes usar fácilmente Mockito para simular una API REST en Spring Boot .

Ponga un controlador de código en su árbol de prueba:

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

Su cliente deberá llamar a la API en localhost cuando ejecute las pruebas. Esto podría configurarse en src/test/resources/application.properties . Si la prueba usa RANDOM_PORT , su cliente bajo prueba tendrá que encontrar ese valor. Esto es un poco complicado, pero el problema se aborda aquí: Spring Boot: cómo obtener el puerto en ejecución

Configure su clase de prueba para usar un WebEnvironment (un servidor en ejecución) y ahora su prueba puede usar Mockito de la manera estándar, devolviendo objetos ResponseEntity según sea necesario:

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

También puede usar esto para realizar pruebas de extremo a extremo de todo su microservicio en un entorno con el simulacro creado anteriormente. Una forma de hacer esto es inyectar TestRestTemplate en su clase de prueba, y usarlo para llamar a su API REST en lugar de clientFunctionUnderTest del ejemplo.

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

Como funciona esto

Debido a que OtherApiHooks es un @RestController en el árbol de prueba, Spring Boot establecerá automáticamente el servicio REST especificado cuando se ejecute SpringBootTest.WebEnvironment .

Mockito se usa aquí para burlarse de la clase del controlador, no del servicio en su totalidad. Por lo tanto, habrá un procesamiento del lado del servidor gestionado por Spring Boot antes de que se aplique la simulación. Esto puede incluir cosas como deserializar (y validar) la ruta UUID que se muestra en el ejemplo.

Por lo que puedo decir, este enfoque es robusto para pruebas paralelas con IntelliJ y Maven.


Si desea realizar una prueba unitaria de su cliente, entonces se burlaría de los servicios que están realizando las llamadas a la API REST, es decir, con mockito . Supongo que tiene un servicio que está realizando esas llamadas a la API para usted, ¿verdad?

Si, por otro lado, quiere "simular" las APIs restantes en el sentido de que hay algún tipo de servidor que le da respuestas, lo que estaría más en línea en las pruebas de integración, podría probar uno de los muchos frameworks que hay, como restito , rest-driver o betamax .