Open
Description
在尝到过自动化测试的甜头之后,我基本所有的项目都会有配套的自动化测试,之所这里没有说测试驱动开发(Test Driven Development
),因为我目前确实还没做到TDD
,我目前还是先Develop
,然后才写Test
,当然我还是希望以后真的能切实做到TDD
,因为我一直相信前人经过很多弯路之后留下了的一些经验。
之前我的项目没太多的对第三方服务的依赖,所以写测试的时候,也没有太操心和第三方接口的一些问题,但是这次一个新项目对第三方服务严重依赖,因为基本所有的内容都来自三方服务,但是因为第三方服务也在开发中,就导致他们的接口很不稳定,这也让我的测试用例时通时不通,于是经过一些调研,找到了一些合适的Spring Boot
下面基于MockRestServiceServer
的模拟第三方服务接口的方法。本项目所有代码都在这里。
业务代码
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class MainController {
private RestTemplate restTemplate;
@Autowired
public MainController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public @ResponseBody
String demo() {
String result = restTemplate.getForObject("http://some_third_api_service.com", String.class);
return String.format("{\"result\": %s}", result);
}
}
上面基本上就是这个demo项目的所有业务代码了,这里之所以把DemoApplication.class
也贴出来,是为了强调这里的RestTemplate
这个Bean
,因为如果想要用后面的MockRestServiceServer
,这里就需要有一个这样的Bean
,用于在测试的代码里替换它,如果Controller
里面的restTemplate
是在demo()
方法里new
出来的,就无法实现模拟了,读者可以自己尝试一下。
测试代码
package com.example.demo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.support.RestGatewaySupport;
import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MainControllerTest {
@Autowired
RestTemplate restTemplate;
@Autowired
private MockMvc mvc;
private MockRestServiceServer mockServer;
@Before
public void setUp() {
RestGatewaySupport gateway = new RestGatewaySupport();
gateway.setRestTemplate(restTemplate);
mockServer = MockRestServiceServer.createServer(gateway);
}
@Test
public void demoTest() throws Exception {
mockServer.expect(once(), requestTo("http://some_third_api_service.com"))
.andRespond(withSuccess("ok123", MediaType.APPLICATION_JSON));
mvc.perform(MockMvcRequestBuilders
.get("/")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"result\":\"ok123\"}"));
mockServer.verify();
}
}
从这里的setUp()
就可以明白为什么上面的业务代码要用Bean
了,否则这里的reset
也就没有了意义。这也设置之后,我们就可以在withSuccess
写任何我们期望的来自第三方的返回,这也不仅仅可以脱离测试时对第三方的依赖,也可以加快测试的执行速度。
参考资料: