【问题标题】:Spring not autowiring when run from JUnit从 JUnit 运行时 Spring 不会自动装配
【发布时间】:2022-01-21 19:27:45
【问题描述】:

我正在测试我有 EncodeUtil 的 ControllerAdvice 类是自动装配的,当我使用 JUnit 测试运行时,这总是被解析为 Null,通过 Spring Boot 应用程序运行时没有问题,我在这里缺少什么?

@ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private EncodeUtil encodeUtil;

    @Override
    public boolean supports(MethodParameter returnType, 
                            Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> converterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {

        if(returnType.getMethod().getName().equalsIgnoreCase("doPNMImage"))
        {
           encodeUtil.encodeBase64(body);
           response.getHeaders().add("ENCODE_TYPE", "Base64");
        }
        else return body;
    }
}

这是我的 Junit 课程,我有

@ExtendWith(SpringExtension.class)
@WebMvcTest(PNMImageController.class)
@AutoConfigureMockMvc
public class Base64EncodedResponseBodyAdviceTest {

    @Mock
    private EncodeUtil encodeUtil;

    @InjectMocks
    PNMImageController pnmImageController;

    @BeforeEach
    void init(){
      MockitoAnnotations.initMocks(pnmImageController);
      mockMvc = MockMvcBuilders.standaloneSetup(pnmImageController)
            .setControllerAdvice(Base64EncodedResponseBodyAdvice.class)
            .build();
    }

    @Test
    public void getAuthorizeTest() throws Exception {
        ReflectionTestUtils.setField(encodeUtil, "salt", SALT);
        Mockito.when(this.encodeUtil.encodeBase64()).thenReturn(TEST_BASE64_STR);
        mockMvc.perform(post("http://localhost:8080/image/doPNMImage"))
                .andExpect(status().isOk())
                .andExpect(content().string(TEST_BASE64_STR))
                .andExpect(header().string("ENCODE_TYPE", "Base64"));
    }
}

测试失败,出现 Nullpointer 异常

Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.ylm.cnpl.core.util.EncodeUtil.encodeBase64(String)" because "this.encodeUtil" is null
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.ylm.cnpl.core.util.encodeBase64(String)" because "this.encodeUtil" is null
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
    

【问题讨论】:

    标签: spring spring-boot spring-mvc junit mockito


    【解决方案1】:

    @WebMvcTest-javadoc:

    使用此注解将禁用完全自动配置,而是应用仅与 MVC 相关的配置测试(即@Controller、@ControllerAdvice、@JsonComponent、Converter/GenericConverter、过滤器、WebMvcConfigurer 和 HandlerMethodArgumentResolver bean 但不是 @Component、@Service 或 @Repository bean)


    如果我们希望 EncodeUtil 在我们的测试中作为 bean:导入(配置),配置/组件扫描它。

    • 最简单:使用@SpringBootTest 代替@WebMvcTest

    • 为了保持测试上下文“精简”,我们应该:

      • 为受影响的 bean 和import 指定一个(主)配置进行测试。

      • 或/和专用“测试”配置,最简单的是:

        @ExtendWith(SpringExtension.class)
        @WebMvcTest(PNMImageController.class)
        @AutoConfigureMockMvc
        public class Base64EncodedResponseBodyAdviceTest {
        
          @Configuration
          static class CustomTestConfig {
            @Bean
            EncodeUtil  ...               
          }
          ...
        } 
        

    如果我们想让@InjectMocks 工作,我们应该(尝试)@MockBean 而不是@Mock... Difference between @Mock, @MockBean and Mockito.mock() ... Difference between @Mock and @InjectMocks ...

    我会使用:(org.springfr...)@MockBean@Autowired !;)

    【讨论】:

    • 谢谢您的回复,我的目的是测试ControllerAdvice在调用控制器的Post方法时会被调用,使用@SpringBootTest如何启动控制器的REST方法
    • -> spring.io/guides/gs/testing-web ! ;) (MockMvc 或 TestRestTemplate)
    【解决方案2】:

    当您尝试测试一个控制器和 controllerAdvice 时,使用@WebMvcTest(PNMImageController.class) 是正确的选择

    使用@WebMvcTest,您可以依靠 Spring 创建所需的控制器。 您尝试使用 @InjectMocks 创建第二个,但 Spring 尝试构建的那个无法构建。

    参见@WebMvcTest javadoc

    使用此注解将禁用完全自动配置,而是仅应用与 MVC 测试相关的配置(即 @Controller、@ControllerAdvice、@JsonComponent、Converter/GenericConverter、Filter、WebMvcConfigurer 和 HandlerMethodArgumentResolver bean,但不应用 @Component、@Service 或@Repository bean)。

    您需要将EncodeUtil 提供给 Spring。 当您打算将其用作模拟时,要使用的正确注释是 @MockBean

    其次,您创建一个 mockMvc 实例。 无需这样做 - 您明确编写了@AutoConfigureMockMvc,因此您可以将其自动装配到您的测试中。事实上@WebMvcTest 是用@AutoConfigureMockMvc 注解的,所以你的测试中不需要它。 @ExtendWith(SpringExtension.class) 也是如此。

    更新代码

    @WebMvcTest(PNMImageController.class)
    public class Base64EncodedResponseBodyAdviceTest {
    
        @MockBean
        private EncodeUtil encodeUtil;
    
        @Autowired
        MockMvc mockMvc;
    
    
        @Test
        public void getAuthorizeTest() throws Exception {
            // ReflectionTestUtils.setField(encodeUtil, "salt", SALT);
            Mockito.when(this.encodeUtil.encodeBase64()).thenReturn(TEST_BASE64_STR);
            mockMvc.perform(post("http://localhost:8080/image/doPNMImage"))
                    .andExpect(status().isOk())
                    .andExpect(content().string(TEST_BASE64_STR))
                    .andExpect(header().string("ENCODE_TYPE", "Base64"));
        }
    }
    
    

    最重要的是:

    • 在 encodeUtil 上设置盐域看起来很可疑。您不需要在模拟上设置字段。

    恕我直言,将您的测试更改为 @SpringBootTest 不是可行的方法: SpringBootTest 用于集成测试,它将启动您的整个应用程序。当然,它会完成工作——你的控制器也会被启动,但你应该努力让你的测试尽可能精简——只旋转所需的控制器会让你的测试更快,更不脆弱。

    【讨论】:

      【解决方案3】:

      【讨论】:

      • 通过添加@SpringBootTest 会引发以下错误Configuration error: found multiple declarations of @BootstrapWith for test class [com.ylm.cnpl.core.util.Base64EncodedResponseBodyAdviceTest]: [@org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper), @org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.context.SpringBootTestContextBootstrapper)]
      猜你喜欢
      • 2012-08-13
      • 2018-12-01
      • 2011-04-09
      • 2023-03-25
      • 2012-09-13
      • 2020-02-09
      • 1970-01-01
      • 1970-01-01
      • 2013-10-18
      相关资源
      最近更新 更多