java - test - ¿Cómo me burlo de un intercambio de plantillas REST?
spring test (8)
Tengo un servicio en el que necesito pedir información a un servidor externo a través del resto:
public class SomeService {
public List<ObjectA> getListofObjectsA() {
List<ObjectA> objectAList = new ArrayList<ObjectA>();
ParameterizedTypeReference<List<ObjectA>> typeRef = new ParameterizedTypeReference<List<ObjectA>>() {};
ResponseEntity<List<ObjectA>> responseEntity = restTemplate.exchange("/objects/get-objectA", HttpMethod.POST, new HttpEntity<>(ObjectAList), typeRef);
return responseEntity.getBody();
}
}
¿Cómo puedo escribir una prueba JUnit para getListofObjectsA()
?
He intentado con el siguiente:
@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
private MockRestServiceServer mockServer;
@Mock
private RestTemplate restTemplate;
@Inject
private SomeService underTest;
@Before
public void setup() {
mockServer = MockRestServiceServer.createServer(restTemplate);
underTest = new SomeService(restTemplate);
mockServer.expect(requestTo("/objects/get-objectA")).andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{json list response}", MediaType.APPLICATION_JSON));
}
@Test
public void testGetObjectAList() {
List<ObjectA> res = underTest.getListofObjectsA();
Assert.assertEquals(myobjectA, res.get(0));
}
Sin embargo, el código anterior no funciona, muestra que responseEntitty
es null
. ¿Cómo puedo corregir mi prueba para restTemplate.exchange
correctamente restTemplate.exchange
?
Este es un ejemplo con la clase ArgumentMatchers no abatida
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
Este trabajo por mi parte.
ResourceBean resourceBean = initResourceBean();
ResponseEntity<ResourceBean> responseEntity
= new ResponseEntity<ResourceBean>(resourceBean, HttpStatus.ACCEPTED);
when(restTemplate.exchange(
Matchers.anyObject(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity> any(),
Matchers.<Class<ResourceBean>> any())
).thenReturn(responseEntity);
Implementé una pequeña biblioteca que es bastante útil. Proporciona un ClientHttpRequestFactory
que puede recibir algún contexto. Al hacerlo, permite pasar por todas las capas del cliente, como verificar que los parámetros de consulta estén valorados, establecer los encabezados y verificar que la deserialización funcione bien.
La instancia de RestTemplate
tiene que ser un objeto real. Debería funcionar si creas una instancia real de RestTemplate
y la haces @Spy
.
@Spy
private RestTemplate restTemplate = new RestTemplate();
No necesitas el objeto MockRestServiceServer
. La anotación es @InjectMocks
no @Inject
. A continuación se muestra un código de ejemplo que debería funcionar
@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private SomeService underTest;
@Test
public void testGetObjectAList() {
ObjectA myobjectA = new ObjectA();
//define the entity you want the exchange to return
ResponseEntity<List<ObjectA>> myEntity = new ResponseEntity<List<ObjectA>>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(
Matchers.eq("/objects/get-objectA"),
Matchers.eq(HttpMethod.POST),
Matchers.<HttpEntity<List<ObjectA>>>any(),
Matchers.<ParameterizedTypeReference<List<ObjectA>>>any())
).thenReturn(myEntity);
List<ObjectA> res = underTest.getListofObjectsA();
Assert.assertEquals(myobjectA, res.get(0));
}
Para mí, tuve que usar Matchers.any (URI.class)
Mockito.when(restTemplate.exchange(Matchers.any(URI.class), Matchers.any(HttpMethod.class), Matchers.<HttpEntity<?>> any(), Matchers.<Class<Object>> any())).thenReturn(myEntity);
Si su intención es probar el servicio sin preocuparse por la llamada de descanso, sugeriré que no use ninguna anotación en su prueba de unidad para simplificar la prueba.
Por lo tanto, mi sugerencia es refactorizar su servicio para recibir la placa de descanso utilizando el constructor de inyección. Esto facilitará la prueba. Ejemplo:
@Service
class SomeService {
@AutoWired
SomeService(TestTemplateObjects restTemplateObjects) {
this.restTemplateObjects = restTemplateObjects;
}
}
El RestTemplate como componente, para ser inyectado y burlado después de:
@Component
public class RestTemplateObjects {
private final RestTemplate restTemplate;
public RestTemplateObjects () {
this.restTemplate = new RestTemplate();
// you can add extra setup the restTemplate here, like errorHandler or converters
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
}
Y la prueba:
public void test() {
when(mockedRestTemplateObject.get).thenReturn(mockRestTemplate);
//mock restTemplate.exchange
when(mockRestTemplate.exchange(...)).thenReturn(mockedResponseEntity);
SomeService someService = new SomeService(mockedRestTemplateObject);
someService.getListofObjectsA();
}
De esta manera, tiene acceso directo para simular el resto de la plantilla mediante el constructor SomeService.
ResponseEntity<String> responseEntity = new ResponseEntity<String>("sampleBodyString", HttpStatus.ACCEPTED);
when(restTemplate.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<String>> any()
)
).thenReturn(responseEntity);