我想在Spring Boot中模拟一个RestTemplate
,在那里我在一个方法中进行REST调用。要测试我正在创建的微服务的控制器, 我想在微服务的控制器中测试方法。
例如:
@GetMapping(value = "/getMasterDataView", produces = { MediaType.APPLICATION_JSON_VALUE })
@CrossOrigin(origins = { "http://192.1**********" }, maxAge = 3000)
public ResponseEntity<MasterDataViewDTO> getMasterDataView() throws IOException {
final String uri = "http://localhost:8089/*********";
RestTemplate restTemplate = new RestTemplate();
MasterDataViewDTO masterDataViewDTO = restTemplate.getForObject(uri, MasterDataViewDTO.class);
return new ResponseEntity<>(masterDataViewDTO, HttpStatus.OK);
}
如何使用模拟来测试它?
这是我到目前为止所拥有的:
@Test
public void testgetMasterDataView() throws IOException {
MasterDataViewDTO masterDataViewDTO= mock(MasterDataViewDTO.class);
//String uri = "http://localhost:8089/*********";
Mockito.when(restTemplate.getForObject(Mockito.anyString(),ArgumentMatchers.any(Class.class))).thenReturn(masterDataViewDTO);
assertEquals("OK",inquiryController.getMasterDataView().getStatusCode());
}
我在运行模拟时收到错误,方法getMasterDataView()
被调用,其中的 REST 调用也被调用并抛出错误。如何编写测试,以便不调用 REST 终结点?如果可能的话,我想和Mockito一起做这件事。
在开始编写测试之前,您应该稍微更改一下代码。首先,如果你提取那个RestTemplate
,并为它创建一个单独的bean,你会注入到你的控制器中,这会容易得多。
为此,请在@Configuration
类或主类中添加类似以下内容:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
此外,您必须从控制器中删除new RestTemplate()
,并自动连接它,例如:
@Autowired
private RestTemplate restTemplate;
现在您已经完成了此操作,在测试中注入模拟RestTemplate
将容易得多。
对于您的测试,您有两个选择:
要么- 模拟
RestTemplate
,要么使用模拟框架(例如。莫基托) - 或者,您可以使用
MockRestServiceServer
,它允许您编写测试来验证是否正确调用了 URL、请求是否匹配等。
使用莫米托进行测试
要使用 Mockito 模拟您的RestTemplate
,您必须确保在测试中添加以下注释:
@RunWith(MockitoJUnitRunner.class)
之后,您可以执行以下操作:
@InjectMocks
private MyController controller;
@Mock
private RestTemplate restTemplate;
现在,您可以像这样调整测试:
@Test
public void testgetMasterDataView() throws IOException {
MasterDataViewDTO dto = new MasterDataViewDTO();
when(restTemplate.getForObject("http://localhost:8089/*********", MasterDataViewDTO.class)).thenReturn(dto);
ResponseEntity<MasterDataViewDTO> response = controller.getMasterDataView();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo(dto);
}
你可以像在测试中那样嘲笑 DTO,但你不必这样做,而且我认为这样做没有任何好处。你必须嘲笑的是restTemplate.getForObject(..)
电话。
使用MockRestServiceServer
进行测试
另一种方法是使用MockRestServiceServer
.为此,您必须在测试中使用以下注释:
@RunWith(SpringRunner.class)
@RestClientTest
然后你必须自动连接你的控制器和MockRestServiceServer
,例如:
@Autowired
private MyController controller;
@Autowired
private MockRestServiceServer server;
现在你可以像这样编写测试:
@Test
public void testgetMasterDataView() throws IOException {
server
.expect(once(), requestTo("http://localhost:8089/*********"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(new ClassPathResource("my-mocked-result.json"), MediaType.APPLICATION_JSON));
ResponseEntity<MasterDataViewDTO> response = controller.getMasterDataView();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
// TODO: Write assertions to see if the DTO matches the JSON structure
}
除了测试实际的 REST 调用是否匹配之外,这还允许您测试您的 JSON 到 DTO 是否也有效。
您可以使用@RestClientTest
和MockRestServiceServer
来实现此目的。他们的文档中提供了一个示例:
@RunWith(SpringRunner.class)
@RestClientTest(RemoteVehicleDetailsService.class)
public class ExampleRestClientTest {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
public void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
throws Exception {
this.server.expect(requestTo("/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
-
创建 bean,而不是在服务中使用
new RestTemplate()
。 -
对 Bean 使用 Profile("!test"),仅用于非测试配置文件。
-
创建如下测试类:
@SpringBootTest(properties = "spring.profiles.active:test") @ActiveProfiles("test") @RunWith(SpringRunner.class) @Log4j2 @Transactional public class QabzinoMockTest { @Autowired private WebApplicationContext webApplicationContext; @Autowired private QabzinoLogRepository qabzinoLogRepository; private MockMvc mockMvc; private final WebServiceStatus successStatus = new WebServiceStatus(); @Mock private RestTemplate restTemplate; private final Gson mapper = new Gson(); @TestConfiguration static class Config { @Bean public RestTemplate rest() { return Mockito.mock(RestTemplate.class); } } @Before public void setup() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); this.restTemplate = (RestTemplate) this.webApplicationContext.getBean("rest"); LoginOutput responseModel = new LoginOutput(); responseModel.setStatus("200"); Mockito.when( restTemplate.postForEntity( Mockito.eq(LOGIN_URL), Mockito.isA(HttpEntity.class), Mockito.eq(String.class) ) ).thenReturn(ResponseEntity.ok().body(mapper.toJson(responseModel))); } }
在此示例中,我们在static class Config
中创建具有rest
名称的 Bean,并在所有测试之前在设置方法中模拟它。
-
首选测试配置可能对您有用
application-test.properties
:spring.h2.console.enabled=true
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=真;忽略大小写=真;
spring.datasource.username=sa
spring.datasource.password=sa
最后,当我们在代码中看到 restTemplate 时,mock bean 返回我们的响应模型而不是真正的服务调用:)