MockWebServer en WebClient de Spring
En este artículo vamos a profundizar como realizar test integrados utilizando MockWebServer en WebClient de Spring. La utilización de MockWebServer nos va a permitir poder mockear cualquier petición que se haga através de nuestro WebClient.
MockWebServer actúa como un servidor web, al cual intercepta la petición realizada y devuelve una respuesta con un body previamente establecida.
Usando MockWebServer
Dependencias de MockWebServer
Vamos a añadir las siguientes dependencias añadir las librerías necesarias:
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.5.0</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>mockwebserver</artifactId> <version>4.5.0</version> <scope>test</scope> </dependency>
Para poder utilizar las librerías hemos añadido tanto la librería mockwebserver como la de okhttp.
Añadiendo MockWebServer a nuestros test
Vamos a añadir MockWebServer a nuestros test con Spock:
@SpringBootTest(classes = StratioSpringBootService.class) @AutoConfigureMockMvc abstract class ResourceISpec extends Specification { @Autowired protected MockMvc mockMvc static MockWebServer mockBackEnd def setupSpec() { mockBackEnd = new MockWebServer() mockBackEnd.start(37639) } def cleanSpec() { mockBackEnd.shutdown() } }
Como podemos ver en este fragmento de código, hemos añadido la clase MockWebServer la cual ha sido importada como static.
A continuación, tenemos que arrancar el el MockWebServer antes de que comience ningún test a ejecutarse, sería como el @BeforeAll, añadiendo el puerto donde lo queremos levantar. Finalmente, el último paso de configuración sería hacer un shutdown del MockWebServer, una vez se han finalizado todos los test.
A continuación añadimos una clase que extiende de la clase abstracta anterior para hacer uso del MockWebServer.
class CarWorkshopISpec extends ResourceISpec { static final CAR_INFO_PATH = "fixtures/carWorkshop/carInfo.json" static final CAR_LOCATION_PATH = "fixtures/carWorkshop/carLocation.json" @Shared String carInfo @Shared String carLocation def setup() { mockBackEnd.setDispatcher(new Dispatcher() { @Override MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws InterruptedException { if (recordedRequest.getPath().startsWith("/carworkshop/car-info")) { return new MockResponse().setResponseCode(200).setBody(carInfo) } else if (recordedRequest.getPath().startsWith("/carworkshop/car-location")) { return new MockResponse().setResponseCode(200).setBody(carLocation) } } }) carInfo = new ClassPathResource(CAR_INFO_PATH).getFile().getText() carLocation = new ClassPathResource(CAR_LOCATION_PATH).getFile().getText() } def "When a resquest is performed to get car info then a list of cars are returned "() { when: def results = mockMvc.perform(get("/carInfo") ).andReturn().response then: results.status == HttpStatus.OK.value() def expected = "[{\"id\":2,\"code\":\"SAD005\",\"description\":\"Golf red color\",\"address\":Bremen,\"phone\":0067627621,\"postalCode\":00321}]" JSONAssert.assertEquals(new JSONArray(expected), new JSONArray(results.contentAsString), false) } }
En la clase anterior hacemos uso del MockWebServer, añadiendo una serie de json a través de un dispatcher (método dispatch), que depende de la petición que se haga (request). Esta es una manera de añadir una respuesta cuando tenemos diferentes request, pero en cambio, si tenemos una única request lo podemos hacer de una forma directa a través de enqueue.
Añadir Response a MockServer a través de enqueue.
Si tenemos un test integrado en el cual se va a realizar una única llamada podemos realizarlo a través de enqueue:
mockBackEnd.enqueue(new MockResponse().setBody(carInfo).addHeader("Content-Type", "application/json"))
Añadir Reponse a MockServer a través de dispatch
En muchas ocasiones vamos a tener que realizar un test integrado en el cual se realizan varias llamadas con WebClient, para esos casos, será mejor hacer uso del método dispatch:
mockBackEnd.setDispatcher(new Dispatcher() { @Override MockResponse dispatch(@NotNull RecordedRequest recordedRequest) throws InterruptedException { if (recordedRequest.getPath().startsWith("/carworkshop/car-info")) { return new MockResponse().setResponseCode(200).setBody(carInfo) } else if (recordedRequest.getPath().startsWith("/carworkshop/car-location")) { return new MockResponse().setResponseCode(200).setBody(carLocation) } } })
Conclusión
En esta entrada de refactorizando hemos visto como añadir un MockWebServer en un WebClient de Spring, para poder implementarlo en la parte de test integrado de nuestra aplicación.