在Spring集成测试中模拟RestTemplateBuilder和RestTemplate

时间:2018-03-06 10:28:54

标签: java spring mockito resttemplate

我有一个REST资源,可以注入RestTemplateBuilder来构建RestTemplate

public MyClass(final RestTemplateBuilder restTemplateBuilder) {
    this.restTemplate = restTemplateBuilder.build();
}

我想测试那个课程。我需要模拟RestTemplate对其他服务的调用:

request = restTemplate.getForEntity(uri, String.class);

我在我的IT中试过这个:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIT {

@Autowired
private TestRestTemplate testRestTemplate;
@MockBean
private RestTemplateBuilder restTemplateBuilder;
@Mock
private RestTemplate restTemplate;

@Test
public void shouldntFail() throws IOException {

    ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
    when(restTemplateBuilder.build()).thenReturn(restTemplate);
    when(restTemplate.getForEntity(any(URI.class), any(Class.class))).thenReturn(responseEntity);
...
ResponseEntity<String> response = testRestTemplate.postForEntity("/endpoint", request, String.class);
  ...
}
}

当我运行测试时,我得到以下异常:

java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.test.web.client.TestRestTemplate': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: RestTemplate must not be null

我该如何正确地做到这一点?

2 个答案:

答案 0 :(得分:8)

您的问题是执行的顺序。在您有机会在MockBean中设置之前,会创建包含@Test的上下文。解决方案是在将RestTemplateBuilder插入上下文时提供已完全设置的@SpringBootTest。你可以这样做。

将以下内容添加到TestApplication注释中。其中classes = {TestApplication.class, MyIT.ContextConfiguration.class}, 是您的Spring Boot应用程序类。

@Autowired
private TestRestTemplate testRestTemplate;

修改你的类成员,删除你的restTemplate和restTemplateBuilder。

MyIT

将静态内部类添加到@Configuration static class ContextConfiguration { @Bean public RestTemplateBuilder restTemplateBuilder() { RestTemplateBuilder rtb = mock(RestTemplateBuilder.class); RestTemplate restTemplate = mock(RestTemplate.class); when(rtb.build()).thenReturn(restTemplate); return rtb; } } 类:

RestTemplate

在测试中,修改@Test public void someTest() { when(testRestTemplate.getRestTemplate().getForEntity(... } 模拟以执行任何操作:

[{array:[1,2,3,4]},{array2:[1,2,3,4]}]

答案 1 :(得分:-2)

只需使用反射将构建器创建的 restTemplate 替换为模拟的 restTemplate。

例如:

@Mock
private RestTemplate mockRestTemplate;

@Test
public void shouldntFail() throws IOException {
    this.initRestTemplate()

    ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
    when(mockRestTemplate.getForEntity(any(URI.class), any(Class.class))).thenReturn(responseEntity);
}

private void initRestTemplate() {
    Field field = ReflectionUtils.findField(MyClass.class, "restTemplate");
    if (ObjectUtils.isNotEmpty(field)) {
      field.setAccessible(Boolean.TRUE);
      ReflectionUtils.setField(field, *yourObjectWithRestRemplate*, mockRestTemplate);
    }
}

P.S. I used ReflectionUtils provided by Spring, you can use any you prefer.