在我的应用程序中,我有很多REST服务。我已经为所有服务编写了测试:
org.springframework.web.client.RestTemplate
REST服务调用,例如看起来像这样:
final String loginResponse = restTemplate.exchange("http://localhost:8080/api/v1/xy", HttpMethod.POST, httpEntity, String.class)
.getBody();
然后我检查了响应体 - 一切正常。 缺点是必须启动应用程序才能调用REST服务。
我现在的问题是如何在JUnit- @Test方法中做到这一点? 它是一个Spring Boot应用程序(带有嵌入式tomcat)。
感谢您的帮助!
答案 0 :(得分:16)
在文档中有一个很好的chapter,我建议你仔细阅读它以完全理解你能做什么。
我喜欢将@IntegrationTest
与自定义配置一起使用,因为它启动整个服务器并允许您测试整个系统。如果你想用mocks替换系统的某些部分,你可以通过排除某些配置或bean并用你自己的替换它们来做到这一点。
这是一个小例子。我遗漏了MessageService
界面,因为IndexController
它的作用很明显,而且它的默认实施 - DefaultMessageService
- 因为它&#39} ; s不相关。
它的作用是将整个应用程序减去DefaultMessageService
而不是MessageService
。然后,它使用RestTemplate
向测试用例中正在运行的应用程序发出真实的HTTP请求。
申请类:
IntegrationTestDemo.java:
@SpringBootApplication
public class IntegrationTestDemo {
public static void main(String[] args) {
SpringApplication.run(IntegrationTestDemo.class, args);
}
}
IndexController.java:
@RestController
public class IndexController {
@Autowired
MessageService messageService;
@RequestMapping("/")
String getMessage() {
return messageService.getMessage();
}
}
测试类:
IntegrationTestDemoTest.java:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfig.class)
@WebIntegrationTest // This will start the server on a random port
public class IntegrationTestDemoTest {
// This will hold the port number the server was started on
@Value("${local.server.port}")
int port;
final RestTemplate template = new RestTemplate();
@Test
public void testGetMessage() {
String message = template.getForObject("http://localhost:" + port + "/", String.class);
Assert.assertEquals("This is a test message", message);
}
}
TestConfig.java:
@SpringBootApplication
@ComponentScan(
excludeFilters = {
// Exclude the default message service
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = DefaultMessageService.class),
// Exclude the default boot application or it's
// @ComponentScan will pull in the default message service
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = IntegrationTestDemo.class)
}
)
public class TestConfig {
@Bean
// Define our own test message service
MessageService mockMessageService() {
return new MessageService() {
@Override
public String getMessage() {
return "This is a test message";
}
};
}
}
答案 1 :(得分:9)
如果您没有寻找端到端(integretion)测试,MockRestServiceServer
可能会对您有所帮助。我发现将测试用例与实际服务分离是非常有用的。
Spring doc说:
用于涉及直接或间接使用RestTemplate的测试。提供一种方法来设置将通过RestTemplate执行的预期请求以及模拟响应,以便 删除对实际服务器的需求 。
以下是official doc
还有一个提示是,requestTo
无法自动导入
server.expect(manyTimes(), requestTo("/hotels/42")) ....
这是org.springframework.test.web.client.match.MockRestRequestMatchers
答案 2 :(得分:7)
由于您使用Spring MVC for REST,我建议使用实例化MockMVC()提供的测试工具 - 启用测试,例如:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
... // any required Spring config
)
@WebAppConfiguration
public class RestControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void getUserList() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(content().encoding("UTF-8"))
.andExpect(jsonPath("$", hasSize(8)))
.andExpect(jsonPath("$[0].id").exists())
.andExpect(jsonPath("$[0].alias").exists())
.andExpect(jsonPath("$[0].name").exists())
);
}
}
此单元测试将测试REST接口而不进行部署。具体来说,是否返回了8个用户,而第一个用户是否包含字段','别名'和' name'。
jsonPath断言需要两个依赖项:
'com.jayway.jsonpath:json-path:0.8.1'
'com.jayway.jsonpath:json-path-assert:0.8.1'
也可能还有:
'org.springframework:spring-test:4.1.7.RELEASE'
答案 3 :(得分:1)
如果使用Spring Boot,则用RestTemplate
注释测试即可轻松设置所有内容以测试@RestClientTest
。这样可以确保自动配置应用程序的必需部分(RestTemplateBuilder
,ObjectMapper
,MockRestServiceServer
等),以测试客户端类,例如:
@Component
public class UserClient {
private final RestTemplate restTemplate;
public UserClient(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.rootUri("https://reqres.in").build();
}
public User getSingleUser(Long id) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
return this.restTemplate
.exchange("/api/users/{id}", HttpMethod.GET, requestEntity, User.class, id)
.getBody();
}
}
相应的测试(使用JUnit 5)如下所示:
@RestClientTest(UserClient.class)
class UserClientTest {
@Autowired
private UserClient userClient;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private MockRestServiceServer mockRestServiceServer;
@Test
public void userClientSuccessfullyReturnsUserDuke() throws Exception {
String json = this.objectMapper
.writeValueAsString(new User(new UserData(42L, "duke@java.org", "duke", "duke", "duke")));
this.mockRestServiceServer
.expect(requestTo("/api/users/42"))
.andRespond(withSuccess(json, MediaType.APPLICATION_JSON));
User result = userClient.getSingleUser(42L);
assertEquals(42L, result.getData().getId());
assertEquals("duke", result.getData().getFirstName());
assertEquals("duke", result.getData().getLastName());
assertEquals("duke", result.getData().getAvatar());
assertEquals("duke@java.org", result.getData().getEmail());
}
}
此设置可让您使用MockRestServiceServer
指定存根HTTP响应。
如果想了解更多信息,我为此提供了更多detailed tutorial。