source

REST 템플릿 교환을 모의하려면 어떻게 해야 하나요?

bestscript 2023. 2. 12. 18:07

REST 템플릿 교환을 모의하려면 어떻게 해야 하나요?

외부 서버에 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));
}

그러나 위의 코드는 동작하지 않습니다.responseEntittynull적절한 모의고사를 하려면 어떻게 해야 하나요?restTemplate.exchange?

필요없습니다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));
    }

이것은 권장되지 않는ArgumentMatchers 클래스의 예입니다.

when(restTemplate.exchange(
                ArgumentMatchers.anyString(),
                ArgumentMatchers.any(HttpMethod.class),
                ArgumentMatchers.any(),
                ArgumentMatchers.<Class<String>>any()))
             .thenReturn(responseEntity);
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);

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

이건 내 편이야.

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

RestTemplate인스턴스는 실제 객체여야 합니다.의 실제 인스턴스를 작성하면 동작합니다.RestTemplate만들어 내다@Spy.

@Spy
private RestTemplate restTemplate = new RestTemplate();

나는 그런 실수를 하곤 했다.나는 더 신뢰할 수 있는 해결책을 찾았다.수입 명세서도 말씀드렸는데, 저에게도 효과가 있었습니다.다음 코드 조각은 시스템 플레이트를 완벽하게 모킹합니다.

import org.syslogito 를 지정합니다.매처
import static org.syslogito.Matchers.any;

    HttpHeaders headers = new Headers();
    headers.setExpires(10000L);     
    ResponseEntity<String> responseEntity = new ResponseEntity<>("dummyString", headers, HttpStatus.OK);
    when(restTemplate.exchange( Matchers.anyString(), 
            Matchers.any(HttpMethod.class),
            Matchers.<HttpEntity<?>> any(), 
            Matchers.<Class<String>> any())).thenReturn(responseEntity);

restTemplate를 조롱하는 동안 이 문제가 발생한 경우.교환(...) 문제인 것 같습니다.예를 들어, 다음이 작동하지 않습니다.

when(ecocashRestTemplate.exchange(Mockito.any()
                , Mockito.eq(HttpMethod.GET)
                , Mockito.any(HttpEntity.class)
                , Mockito.<Class<UserTransaction>>any())
        ).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));

하지만 이 방법은 실제로 효과가 있습니다.

  ResponseEntity<UserTransaction> variable = new ResponseEntity<>(transaction, HttpStatus.OK);
  when(ecocashRestTemplate.exchange(Mockito.anyString()
                , Mockito.eq(HttpMethod.GET)
                , Mockito.any(HttpEntity.class)
                , Mockito.<Class<UserTransaction>>any())
   ).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));

모키토에 주목하세요.두 번째 블록의 anyString()과 Mockito.any()를 비교합니다.

다음과 같은 교환 전화가 있다고 가정해 보겠습니다.

String url = "/zzz/{accountNumber}";

Optional<AccountResponse> accResponse = Optional.ofNullable(accountNumber)
        .map(account -> {


            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("Authorization", "bearer 121212");


            HttpEntity<Object> entity = new HttpEntity<>(headers);
            ResponseEntity<AccountResponse> response = template.exchange(
                    url,
                    GET,
                    entity,
                    AccountResponse.class,
                    accountNumber
            );

            return response.getBody();
        });

테스트 케이스에서 이를 조롱하기 위해 다음과 같이 mocitko를 사용할 수 있습니다.

when(restTemplate.exchange(
        ArgumentMatchers.anyString(),
        ArgumentMatchers.any(HttpMethod.class),
        ArgumentMatchers.any(),
        ArgumentMatchers.<Class<AccountResponse>>any(),
        ArgumentMatchers.<ParameterizedTypeReference<List<Object>>>any())
)

restTemplate를 사용하여 외부 시스템과 통신하는 클라이언트를 테스트하는 경우 유닛 테스트의 일부로 송신하는httpEntity, 헤더 및 파라미터를 확인해야 합니다.

이러한 상황에서는 Argument Captor가 유용합니다.예를 들어 다음과 같습니다(작업 코드).

@Mock
private RestTemplate restTemplate;

@InjectMocks
private MyClient client;

@Captor
ArgumentCaptor<HttpEntity<?>> httpEntityCaptor;

when(restTemplate.exchange(eq(expectedUrl), eq(HttpMethod.POST), Matchers.any(HttpEntity.class), eq(MyTargetResponse.class)).thenReturn(expectedResponse);



verify(restTemplate).exchange(eq(expectedUrl),eq(HttpMethod.POST), httpEntityCaptor.captor(),eq(MyTargetResponse.class));

HttpEntity<?> actualResponse = httpEntityCaptor.getValue();

HttpHeaders actualResponse.getHeaders();

assertEquals(headers.getFirst("Content-Type", "application/json")

이제 전송된 캡처된 개체를 받았기 때문에 사용 사례에 따라 어설션을 수행할 수 있습니다.

아래 권장되지 않는 ArgumentMatchers를 사용할 수 있습니다.

lenient().when(restTemplate.exchange(ArgumentMatchers.any(String.class), 
ArgumentMatchers.eq(HttpMethod.GET),
ArgumentMatchers.any(), 
ArgumentMatchers.eq(new ParameterizedTypeReference<List<ObjectA>>() {
})))
.thenReturn(responseEntity);

나는 꽤 유용한 작은 도서관을 구현했다.이 기능을 통해ClientHttpRequestFactory콘텍스트를 수신할 수 있습니다.이를 통해 쿼리 파라미터가 평가되고 있는지, 헤더가 설정되어 있는지, 역직렬화가 정상적으로 동작하는지 등의 모든 클라이언트레이어를 확인할 수 있습니다.

사용하시는 경우RestTemplateBuilder통상의 일이 아닐 수도 있어요이것을 시험 시간에 언제와 함께 추가해야 합니다.

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

아직 이 문제를 겪고 있는 사람이 있다면 캡터 주석이 도움이 되었습니다.

@Captor
private ArgumentCaptor<Object> argumentCaptor;

그 후 다음과 같이 요청을 조롱할 수 있었습니다.

ResponseEntity<YourTestResponse> testEntity = new ResponseEntity<>(
    getTestFactoryResponse(), 
    HttpStatus.OK);
when(mockRestTemplate.exchange((String) argumentCaptor.capture(), 
    (HttpMethod) argumentCaptor.capture(), 
    (HttpEntity<?>) argumentCaptor.capture(), 
    (Class<YourTestResponse.class>) any())
).thenReturn(testEntity);

이 특정 exchange()의 경우 stub 대신 stub으로 하는 것이 더 쉽다는 것을 알게 되었습니다.good old override:

var restTemplate = new RestTemplate() {
  public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
                                              Class<T> responseType) throws RestClientException {
            throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
        }
    };

모조품을 줄이다특히 api는 항상 변경됩니다.eq(..) any()..기타.

stubbed exchange() 내의 arg를 체크한 후 반환하거나 예외를 발생시킬 수 있습니다.

그것이 그 엄격한 질문에 대한 답이 아니라는 것을 알고 있다.하지만 결과는 똑같다.코드도 적고 지원도 간단합니다.

mockito-core-2.23.4 탑재

ResponseEntity<YOUR_CLASS> responseEntity = new ResponseEntity(YOUR_CLASS_OBJECT, HttpStatus.OK);

        when(restTemplate.exchange(Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.<ParameterizedTypeReference<YOUR_CLASS>> any()))
                .thenReturn(responseEntity);

- 이런 식으로 하는 거였어 - 모키토when(restTemplate).(URI) any() 、 ( HttpMethod ) any() 、 ( HttpEntity < ) 。>) any() , (Class) any() ).그러면 Return(responseEntity);

그러나 위의 코드는 작동하지 않습니다.responseEntitty가 null임을 나타냅니다.restTemplate를 적절하게 모킹하도록 테스트를 수정하려면 어떻게 해야 합니까?교환할 수 있습니까?

다음을 사용하여 null을 반환합니다.

@Mock
private RestTemplate restTemplate;

그 목적이 조롱하는 것이라면MockRestServiceServerMockito을 사용하다

@Autowired
private RestTemplate restTemplate;

★★★★★★★★★★★★★★★★★」RestTemplate restTemplate = new RestTemplate()가 ""에 MockRestServiceServer ★★★★★★★★★★★★★★★★★」SomeService.

예를 들어 다음과 같습니다.

@Test
public void methodWithPostCallTest() throws URISyntaxException {
    RestTemplate restTemplate = new RestTemplate();
    MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
    mockServer.expect(ExpectedCount.once(), 
            requestTo(new URI("post-method-url")))
            .andExpect(method(HttpMethod.POST))
            .andRespond(withStatus(HttpStatus.OK)
            .contentType(MediaType.APPLICATION_JSON)
            .body("response-body")
          );

    YourService yourService = new YourService(restTemplate);
    String response = yourService.methodWhichExecutesPostCall();

    mockServer.verify();
    assertEquals("response-body", response);  
}

「」를 사용합니다.MockRestServiceServerRestTemplate 메서드에 대한 모의 응답을 반환합니다.POST- - -postForEntity,postForObject ★★★★★★★★★★★★★★★★★」exchange.

상세:클라이언트 응용 프로그램 테스트

만약 당신이 나머지 통화에 신경 쓰지 않고 서비스를 테스트하고자 한다면, 나는 테스트를 간소화하기 위해 유닛 테스트에 주석을 사용하지 말 것을 제안합니다.

그래서 제가 제안하는 것은 주입 컨스트럭터를 사용하여 rest template를 받을 수 있도록 당신의 서비스를 리팩터링하는 것입니다.이렇게 하면 테스트가 쉬워집니다.예:

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

이렇게 하면 Some Service 컨스트럭터가 나머지 템플릿을 직접 시뮬레이션할 수 있습니다.

언급URL : https://stackoverflow.com/questions/39486521/how-do-i-mock-a-rest-template-exchange