在春季集成测试中模拟 RestTemplateBuilder 和 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

如何正确执行此操作?

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

将以下内容添加到@SpringBootTest批注中。其中TestApplication是你的 Spring Boot 应用程序类。

classes = {TestApplication.class, MyIT.ContextConfiguration.class},

因此修改您的类成员,删除您的 restTemplate 和 restTemplateBuilder。

@Autowired
private TestRestTemplate testRestTemplate;

将静态内部类添加到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(...
}

我遇到了类似的问题。我的服务类在构造函数中RestTemplateBuilderRestTemplate作为私有领域,我想嘲笑RestTemplate

在我的服务类 Bean 创建后ReflectionTestUtils我解决了使用 Spring 类来更改我的私有字段 RestTemplate 的问题。

@Mock
private RestTemplate restTemplate;
@Autowired
private MyServiceClass myServiceClass;
@BeforeEach
void setUp() {
    ReflectionTestUtils.setField(myServiceClass, "restTemplate", restTemplate);
}

只需使用反射将构建器创建的 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.

相关内容

  • 没有找到相关文章

最新更新