如何模拟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();
    }
}

如何为getListofObjectsA()编写JUnit测试?

我试过了:

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

这是一个未被弃用的ArgumentMatchers类

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

您不需要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));
    }
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语句。下面这段代码完美地模拟了restemplate。


进口org.mockito.Matchers;

    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.exchange(…)时遇到此问题,则问题似乎与匹配器有关。例如:

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

注意第二个块上的Mockito.anyString()与mockito .any()。

当我们使用restTemplate测试与外部系统通信的客户端时,作为单元测试的一部分,我们需要验证我们发送的httpEntity, header和参数。

ArgumentCaptor在这些情况下很方便。这是我的例子(工作代码)

@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 headers = actualResponse.getHeaders();
assertEquals(headers.getFirst("Content-Type"), "application/json");

现在可以根据您的用例做出断言,因为您已经获得了被发送的捕获对象。

假设您有一个如下的交换调用:

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

您可以使用以下未弃用的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可能是通常的事情不会工作。您需要将它与when(condition)一起添加到测试类中。

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

如果有人仍然面临这个问题,Captor注释为我工作

@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()例子,我发现存根更简单,旧的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(. .)任何(). .等。

您可以在返回某些内容或抛出异常之前检查存根exchange()中的arg。

——我知道这不是那个严格问题的答案。但结果是一样的。代码更少&

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

是让它这样工作- Mockito.when(restTemplate.exchange((URI) any(), (HttpMethod) any(), (HttpEntity) any(),(类)any()))).thenReturn (responseEntity);

然而,上面的代码不起作用,它表明responseentity是null。我怎样才能正确地修改我的测试呢restTemplate.exchange吗?

返回null,因为使用:

@Mock
private RestTemplate restTemplate;

如果目标是用MockRestServiceServer而不是Mockito来模拟,它应该是:

@Autowired
private RestTemplate restTemplate;

RestTemplate restTemplate = new RestTemplate()MockRestServiceServerSomeService提供相同的实例时,

例如:

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

使用MockRestServiceServer,它将为POST调用的任何RestTemplate方法返回模拟响应- postForEntity, postForObjectexchange

更多细节:测试客户端应用程序

如果您的意图是测试服务而不关心rest调用,我建议不要在单元测试中使用任何注释,以简化测试。

所以,我的建议是重构你的服务,使用注入构造函数接收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构造函数直接访问rest模板。

相关内容

  • 没有找到相关文章

最新更新