Как мне высмеять обмен шаблонами REST?

У меня есть служба, в которой мне нужно запросить внешний сервер через отдых для получения некоторой информации:

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

Как я могу написать тест JUnit для getListofObjectsA()?

Я попробовал следующее:

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

Однако приведенный выше код не работает, он показывает, что responseEntitty есть null. Как я могу исправить свой тест, чтобы правильно издеваться над restTemplate.exchange?

Ответ 1

Вам не нужен объект MockRestServiceServer. Аннотация это @InjectMocks не @Inject. Ниже приведен пример кода, который должен работать

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

Ответ 2

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

Ответ 3

Это пример с неотменяемым классом ArgumentMatchers

when(restTemplate.exchange(
                ArgumentMatchers.anyString(),
                ArgumentMatchers.any(HttpMethod.class),
                ArgumentMatchers.any(),
                ArgumentMatchers.<Class<String>>any()))
             .thenReturn(responseEntity);

Ответ 4

Для меня мне пришлось использовать 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);

Ответ 5

Эта работа на моей стороне.

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

Ответ 6

Я внедрил небольшую библиотеку, которая весьма полезна. Он предоставляет ClientHttpRequestFactory, который может получить некоторый контекст. Таким образом, он позволяет проходить через все клиентские уровни, такие как проверка того, что параметры запроса оцениваются, набор заголовков и проверка того, что десериализация работает хорошо.

Ответ 7

Если ваше намерение является тестированием службы без забот о остальном вызове, я предлагаю не использовать аннотацию в unit test для упрощения теста.

Итак, мое предложение реорганизует вашу службу для получения resttemplate с использованием конструктора инъекций. Это облегчит испытание. Пример:

@Service
class SomeService {
    @AutoWired
    SomeService(TestTemplateObjects restTemplateObjects) {
        this.restTemplateObjects = restTemplateObjects;
    }
}

RestTemplate как компонент, который нужно ввести и высмеять после:

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

И тест:

public void test() {

    when(mockedRestTemplateObject.get).thenReturn(mockRestTemplate);

    //mock restTemplate.exchange
    when(mockRestTemplate.exchange(...)).thenReturn(mockedResponseEntity);

    SomeService someService = new SomeService(mockedRestTemplateObject);
    someService.getListofObjectsA();
}

Таким образом, у вас есть прямой доступ к шаблону останова с помощью конструктора SomeService.

Ответ 8

Экземпляр RestTemplate должен быть реальным объектом. Это должно работать, если вы создаете реальный экземпляр RestTemplate и делаете его @Spy.

@Spy
private RestTemplate restTemplate = new RestTemplate();

Ответ 9

Если вы используете RestTemplateBuilder может быть, обычная вещь не будет работать. Вы должны добавить это в свой тестовый класс вместе с when (условие).

@Before    
public void setup() {        
     ReflectionTestUtils.setField(service, "restTemplate", restTemplate);    
}