【问题标题】:How to mock beans Autowired on service layer in @WebMvcTest如何在@WebMvcTest 的服务层上模拟自动装配的bean
【发布时间】:2019-09-17 06:17:43
【问题描述】:

我正在 Spring Boot gradle 应用程序中测试 REST API,我使用 @MockBean 的模拟服务返回 null。如果服务类中有一些自动装配的 bean(我使用了构造函数注入),则此模拟服务返回 null。

这里是示例代码(未编译,仅供理解)


    @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

【问题讨论】:

  • 您能否提供一些您的示例代码。以便我们正确理解您的问题。
  • 模拟的默认行为是返回null... 你必须在模拟上注册行为如果一个方法被调用该怎么办,如果你不这样做,它将返回@987654326 @ 或空集合等。
  • 伙计们,请在更新的问题陈述中循环。
  • @M.Deinum 你的意思是我们应该像when().thenReturn()那样使用BDD吗?我也试过这个。如果是你说的情况,如何处理这种情况,它的基本用例是验证响应体的内容。

标签: spring-boot testing mockito springmockito


【解决方案1】:

检查您的存储库确认了来自问题 cmets 讨论的假设。

您在模拟中指定期望

MyModel requestMessage = new MyModel();
requestMessage.setMessage("Hello Request Post");
given(testService1.getMessage(requestMessage)).willReturn(responseMessage);

但是在您的@WebMvcTest 中的控制器中收到的消息不等于测试中指定的 requestMessage。这是因为MyModel 类没有覆盖equals 方法。 在这种情况下,Mockito 将使用其默认行为:

默认情况下,对于所有返回值的方法,mock 将根据需要返回 null、原始/原始包装器值或空集合。例如 0 表示 int/Integer,false 表示 boolean/Boolean。

您有两种解决问题的方法:

  1. 在您的请求类中覆盖 equals(和 hashCode)。
  2. 熟悉参数匹配器

关于选项 2 的更多信息:

从技术上讲,您的期望相当于:

given(testService1.getMessage(ArgumentMatchers.eq(requestMessage)))
  .willReturn(responseMessage);

您可以使用其他匹配器,甚至定义自己的匹配器。如果您无法修改参数类型的代码(来自 3-rd 方库等的类型),这将非常有用。 例如,您可以使用ArgumentMatchers.any(MyModel.class))

【讨论】:

  • 感谢您的详细解释,我认为我们更接近解决方案。但我尝试了你建议的方式,我的观察是,它只有在我做given(testService1.getMessage(ArgumentMatchers.any(MyModel.class))).willReturn(responseMessage); 时才有效,我认为它很明显。请在 git repository 中找到更新后的代码。
  • 耶,终于成功了。在覆盖方法中这是一个愚蠢的错误。@Lesiak 非常感谢你
  • @Psp360 学习了如何手动实现 equals 和 hashCode 是件好事。好的 IDE 可以为您生成它们(至少对于非循环数据结构)
  • 是的,我从未将它与 mockito 一起使用。但也有一个观察,请看看。链接在上一条评论中。
猜你喜欢
  • 1970-01-01
  • 2016-01-22
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
  • 2017-05-31
  • 2013-08-22
相关资源
最近更新 更多