Java 如何模拟REST模板交换?

Java 如何模拟REST模板交换?,java,spring-boot,junit,mockito,Java,Spring Boot,Junit,Mockito,我有一项服务,我需要通过rest向外部服务器询问一些信息: public class SomeService { public List<ObjectA> getListofObjectsA() { List<ObjectA> objectAList = new ArrayList<ObjectA>(); ParameterizedTypeReference<List<ObjectA>> typ

我有一项服务,我需要通过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();
    }
}

但是,上面的代码不起作用,它显示
responseEntity
null
。如何更正测试以正确模拟
restemplate.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));
    }
@RunWith(MockitoJUnitRunner.class)
公共类服务测试{
@嘲弄
私有RestTemplate RestTemplate;
@注射模拟
私人服务测试不足;
@试验
public void testGetObjectAList(){
ObjectA myobjectA=新ObjectA();
//定义希望exchange返回的实体
ResponseEntity myEntity=新的ResponseEntity(HttpStatus.ACCEPTED);
Mockito.when(restemplate.exchange(
Matchers.eq(“/objects/get objectA”),
Matchers.eq(HttpMethod.POST),
Matchers.any(),
Matchers.any())
).然后返回(myEntity);
List res=underTest.getListofObjectsA();
Assert.assertEquals(myobjectA,res.get(0));
}
ResponseEntity ResponseEntity=新的ResponseEntity(“sampleBodyString”,HttpStatus.ACCEPTED);
何时(restemplate.exchange)(
Matchers.anyString(),
Matchers.any(HttpMethod.class),

Matchers.对我来说,我必须使用Matchers.any(URI.class)


Mockito.when(restemplate.exchange(Matchers.any(URI.class))和Matchers.any(HttpMethod.class),Matchers.我实现了一个非常有用的工具。它提供了一个可以接收一些上下文的
clienthtprequestfactory
。通过这样做,它可以检查所有客户端层,例如检查查询参数是否有值,是否设置了头,以及检查反序列化是否工作正常。

如果您打算在没有c的情况下测试服务关于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模板。

这是我的工作

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);
ResourceBean ResourceBean=initResourceBean();
响应响应响应响应响应响应
=新响应属性(resourceBean,HttpStatus.ACCEPTED);
何时(restemplate.exchange)(
Matchers.anyObject(),
Matchers.any(HttpMethod.class),
Matchers.any(),
Matchers.any())
).然后返回(响应性);

这是一个非弃用类的示例

when(restemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.any())
.然后返回(响应性);

RestTemplate
实例必须是真实对象。如果您创建了
RestTemplate
的真实实例并使其成为
@Spy
,则该实例应该可以工作

@Spy
private RestTemplate restTemplate = new RestTemplate();

如果您使用的是
restemplatebuilder
,则通常无法正常工作。您需要在测试类中添加此选项以及when(条件)


假设您有一个交换电话,如下所示:

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

stringurl=“/zzz/{accountNumber}”;
可选的注册响应=可选的可注册数量(accountNumber)
.map(帐户->{
HttpHeaders=新的HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
标题集(“授权”、“持有人121212”);
HttpEntity=新的HttpEntity(标题);
ResponseEntity response=template.exchange(
网址,
得到,
实体,
AccountResponse.class,
帐号
);
返回response.getBody();
});
要在测试用例中模拟这一点,您可以使用mocitko,如下所示:

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

when(restemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.any(),
ArgumentMatchers.any())
)

我曾经遇到过这样的错误。我找到了一个更可靠的解决方案。我也提到了对我有效的导入语句。下面的代码完全模拟了重新模板

导入org.mockito.Matchers;
导入静态org.mockito.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);
HttpHeaders=newheaders();
标题。设置过期(10000L);
ResponseEntity ResponseEntity=新的ResponseEntity(“dummyString”,标题,HttpStatus.OK);
当(restTemplate.exchange)(Matchers.anyString()),
Matchers.any(HttpMethod.class),

Matchers.如果有人仍然面临这个问题,Captor annotation为我工作

@Captor
private ArgumentCaptor<Object> argumentCaptor;
@Captor
私人辩手辩手;
然后我可以通过以下方式模拟请求:

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);
ResponseEntity testEntity=新的ResponseEntity(
getTestFactoryResponse(),
HttpStatus.OK);
当(mockrestemplate.exchange((字符串)argumentCaptor.capture()时,
(HttpMethod)argumentCaptor.capture(),
(HttpEntity)argumentCaptor.capture(),
(类)any())
).然后返回(测试实体);

如果有人在尝试模拟restTemplate.exchange(…)时遇到此问题,则问题似乎出在matchers上。例如:以下操作不起作用

when(ecocashRestTemplate.exchange(Mockito.any()
                , Mockito.eq(HttpMethod.GET)
                , Mockito.any(HttpEntity.class)
                , Mockito.<Class<UserTransaction>>any())
        ).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));
when(ecocashRestTemplate.exchange(Mockito.any)()
,Mockito.eq(HttpMethod.GET)
,Mockito.any(HttpEntity.class)
,Mockito.any())
).然后返回(新的响应属性(事务,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));
Re
    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);
@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);
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));