如何模拟REST模板交换?

时间:2016-09-14 09:06:41

标签: java spring-boot junit mockito

我有一项服务,我需要通过休息向外部服务器询问一些信息:

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

14 个答案:

答案 0 :(得分:22)

您不需要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));
    }

答案 1 :(得分:15)

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

答案 2 :(得分:9)

这是一个未经处理的ArgumentMatchers

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

答案 3 :(得分: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);

答案 4 :(得分:2)

这项工作在我身边。

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

答案 5 :(得分:1)

假设您像下面这样进行通话:

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

答案 6 :(得分:0)

我实施了非常有用的a small library。它提供了ClientHttpRequestFactory,可以接收一些上下文。通过这样做,它允许遍历所有客户端层,例如检查查询参数是否已被评估,标头设置,以及检查反序列化是否正常。

答案 7 :(得分:0)

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

所以,我的建议是重构你的服务以使用注入构造函数接收resttemplate。这将有助于测试。例如:

area_id FK

将RestTemplate作为组件,在以下情况下进行注入和模拟:

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

测试:

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

通过这种方式,您可以直接访问SomeService构造函数来模拟其余模板。

答案 8 :(得分:0)

RestTemplate实例必须是真实的对象。如果创建RestTemplate的真实实例并将其设置为@Spy,它应该可以工作。

@Spy
private RestTemplate restTemplate = new RestTemplate();

答案 9 :(得分:0)

如果您使用的是RestTemplateBuilder,则可能无法正常进行。您需要将其与when(condition)一起添加到测试类中。

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

答案 10 :(得分:0)

我曾经遇到过这样的错误。我找到了更可靠的解决方案。我也提到了对我有用的导入声明。下面的代码完美地模拟了模板。

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

答案 11 :(得分:0)

如果仍然有人遇到此问题,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);

答案 12 :(得分:0)

如果有人在尝试模拟 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() 与 theMockito.any()。

答案 13 :(得分:0)

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

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 actualResponse.getHeaders();

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

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