【发布时间】:2019-05-15 06:11:31
【问题描述】:
作为 TDD 的一部分,我希望能够测试我的 SpringBoot REST 应用程序的每个部分。但是我无法模拟外部呼叫。
应用结构
1.很少有内部调用外部休息端点的休息端点。
2. 对外部端点的所有调用都通过本地 http 客户端进行编排,该客户端使用 RestTemplate 作为 httpClient。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)
public class TestDrivenDevelopmentWithJavaApplicationTests {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@MockBean
private RestTemplate client;
@Before
public void setup() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
Structure1Root category = new Structure1Root();
Category cat = new Category();
cat.setCategoryName("Test1");
cat.setDescription("Test");
category.setD(cat);
Mockito.when(client.exchange(
ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
ArgumentMatchers.eq(Structure1Root.class)))
.thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));
}
@Test
public void testendpoint1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));
}
}
即使我在 client.exchange(RestTemplate.exchange) 上设置了模拟,我看到 client.exchange 返回的响应为空,而不是 thenReturn 中指定的响应
控制器代码
@RestController
@RequestMapping(path = Endpoint.base)
public class Endpoint {
public static final String base = "/api";
@Autowired
MyHttpClient<Structure2Root> client;
@Autowired
MyHttpClient<Structure1Root> Cclient;
@GetMapping(path = "/endpoint1")
public ResponseEntity<Structure2Root> callEndpt1(@RequestParam String token) {
Response<Structure2Root> resp = client
.execute("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json", Structure2Root.class);
return new ResponseEntity<Structure2Root>(resp.getResponse(), HttpStatus.OK);
}
@GetMapping(path = "/endpoint2")
public ResponseEntity<Structure1Root> callEndpt2(@RequestParam String token) {
Response<Structure1Root> resp = Cclient.execute(
"https://services.odata.org/V2/Northwind/Northwind.svc/Categories(1)?$format=json", Structure1Root.class);
return new ResponseEntity<Structure1Root>(resp.getResponse(),HttpStatus.OK);
}
}
最后,本地http客户端代码
@Service
public class MyHttpClient<O> {
@Autowired
RestTemplate client;
public MyHttpClient() {
// TODO Auto-generated constructor stub
}
public Response<O> execute(String url, Class<O> generic) {
ResponseEntity<O> resp = client.exchange(url, HttpMethod.GET, null, generic);
return new Response<O>(resp.getStatusCode(), resp.getBody());
}
}
这个client.execute 是我打算在第一个代码块中拦截的内容
但是似乎从来没有工作并且总是返回一个空值。 Git Repo
问候,
维拉
【问题讨论】:
-
返回
null是模拟的默认行为。因此,您用于设置模拟的 expactations/predicates 与实际值不匹配。查看您的方法,它需要Structure2Root而不是Structure1Root用于在您的模拟上设置您的预期。 -
@M.Deinum:感谢您的帮助。肯定解决了。一个重要的经验教训是模拟的默认行为。我的误解是我认为默认行为是调用 rest 端点并仅模拟配置了 mockito 的请求。
标签: spring-boot mockito tdd resttemplate mockmvc