如何在@WebMvcTest 的服务层上模拟 beans Autowired
How to mock beans Autowired on service layer in @WebMvcTest
我正在 Spring 启动 gradle 应用程序中测试 REST API,我使用 @MockBean 的模拟服务是 returning null。这个模拟服务 return null 如果服务中有一些自动装配的 bean class(我使用了构造函数注入)。
这里是示例代码(未编译,仅供理解)
@RestController
@RequestMapping("/xxx")
class TestController {
private RetriveDataService retriveDataService;
public TestControllerx(RetriveDataService retriveDataService) {
this.retriveDataService = retriveDataService;
}
@PostMapping(value = "/yyy")
public MyResponseModel myMethod(@RequestBody MyRequestModel model) {
return retriveDataService.retriveData(model);
}
}
@Service
class RetriveDataService {
private TokenService tokenService;
public RetriveDataService(TokenService tokenService) {
this.tokenService = tokenService;
}
public MyResponseModel retriveData(MyRequestModel model) {
String accessToken = tokenService.getToken().getAccessToken();
return retriveData(model, accessToken);
}
}
@RunWith(SpringRunner.class)
@WebMvcTest(TestController.class)
public class TestControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private RetriveDataService retriveDataService;
@Test
public void testRetriveData() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/xxx/yyy").content(objectMapper.writeValueAsString(new MyRequestModel()))
.contentType(MediaType.APPLICATION_JSON_UTF8)).andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
当我 运行 这个测试时,我得到以下输出(如果我的服务不需要另一个 bean,我得到预期的输出)
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
由于此响应,我在 .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) 线上遇到了问题。当我检查响应正文时(因为正文也是空的)
重现问题的示例项目是 here
检查您的存储库确认假设来自问题评论中的讨论。
您在模拟中指定期望值
MyModel requestMessage = new MyModel();
requestMessage.setMessage("Hello Request Post");
given(testService1.getMessage(requestMessage)).willReturn(responseMessage);
但是在您的 @WebMvcTest
中您的控制器中收到的消息不等于测试中指定的 requestMessage。这是因为 MyModel
class 没有覆盖 equals
方法。
在这种情况下,Mockito 将使用其默认行为:
By default, for all methods that return a value, a mock will return either null, a primitive/primitive wrapper value, or an empty collection, as appropriate. For example 0 for an int/Integer and false for a boolean/Boolean.
您有两种解决问题的方法:
- 在您的请求 class.
中覆盖 equals
(和 hashCode
)
- 熟悉参数匹配器
关于选项 2 的更多信息:
从技术上讲,您的期望等同于:
given(testService1.getMessage(ArgumentMatchers.eq(requestMessage)))
.willReturn(responseMessage);
您可以使用其他匹配器,甚至可以定义您自己的匹配器。如果您不能修改参数类型的代码(来自 3-rd 方库等的类型),这将很有用。
例如,您可以使用 ArgumentMatchers.any(MyModel.class))
我正在 Spring 启动 gradle 应用程序中测试 REST API,我使用 @MockBean 的模拟服务是 returning null。这个模拟服务 return null 如果服务中有一些自动装配的 bean class(我使用了构造函数注入)。
这里是示例代码(未编译,仅供理解)
@RestController
@RequestMapping("/xxx")
class TestController {
private RetriveDataService retriveDataService;
public TestControllerx(RetriveDataService retriveDataService) {
this.retriveDataService = retriveDataService;
}
@PostMapping(value = "/yyy")
public MyResponseModel myMethod(@RequestBody MyRequestModel model) {
return retriveDataService.retriveData(model);
}
}
@Service
class RetriveDataService {
private TokenService tokenService;
public RetriveDataService(TokenService tokenService) {
this.tokenService = tokenService;
}
public MyResponseModel retriveData(MyRequestModel model) {
String accessToken = tokenService.getToken().getAccessToken();
return retriveData(model, accessToken);
}
}
@RunWith(SpringRunner.class)
@WebMvcTest(TestController.class)
public class TestControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private RetriveDataService retriveDataService;
@Test
public void testRetriveData() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/xxx/yyy").content(objectMapper.writeValueAsString(new MyRequestModel()))
.contentType(MediaType.APPLICATION_JSON_UTF8)).andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
当我 运行 这个测试时,我得到以下输出(如果我的服务不需要另一个 bean,我得到预期的输出)
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
由于此响应,我在 .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) 线上遇到了问题。当我检查响应正文时(因为正文也是空的)
重现问题的示例项目是 here
检查您的存储库确认假设来自问题评论中的讨论。
您在模拟中指定期望值
MyModel requestMessage = new MyModel();
requestMessage.setMessage("Hello Request Post");
given(testService1.getMessage(requestMessage)).willReturn(responseMessage);
但是在您的 @WebMvcTest
中您的控制器中收到的消息不等于测试中指定的 requestMessage。这是因为 MyModel
class 没有覆盖 equals
方法。
在这种情况下,Mockito 将使用其默认行为:
By default, for all methods that return a value, a mock will return either null, a primitive/primitive wrapper value, or an empty collection, as appropriate. For example 0 for an int/Integer and false for a boolean/Boolean.
您有两种解决问题的方法:
- 在您的请求 class. 中覆盖
- 熟悉参数匹配器
equals
(和 hashCode
)
关于选项 2 的更多信息:
从技术上讲,您的期望等同于:
given(testService1.getMessage(ArgumentMatchers.eq(requestMessage)))
.willReturn(responseMessage);
您可以使用其他匹配器,甚至可以定义您自己的匹配器。如果您不能修改参数类型的代码(来自 3-rd 方库等的类型),这将很有用。
例如,您可以使用 ArgumentMatchers.any(MyModel.class))