【问题标题】:Stubbing methods of injected beans in another bean's constructors在另一个 bean 的构造函数中注入 bean 的存根方法
【发布时间】:2021-09-13 12:12:02
【问题描述】:

我使用构造函数注入测试了一个 Spring bean。注入的 bean 在测试用例中定义为@MockBean,并且还定义了适当的存根:当调用模拟 bean 的某个方法时,它应该返回一个模拟对象。

当我启动测试用例时,我得到一个NullPointerException,因为存根不起作用——该方法总是返回null

这里是要测试的对象的构造函数:

@Autowired
public MyBeanToTest(msTemplate jmsTemplate) throws JMSException {
    this.jmsTemplate = jmsTemplate;
    this.cf = this.jmsTemplate.getConnectionFactory(); // cf is always null
    cf.createSession(); // NPE
}

这是测试用例:

@SpringBootTest
public class MyTestClass {
    @Autowired
    MyBeanToTest myBeanToTest;

    @MockBean
    private JmsTemplate jmsTemplate;
    
    @Mock
    private static ConnectionFactory connectionFactory;

    @Test
    public void testSomething()  {
        ...
        when( jmsTemplate.getConnectionFactory() ).thenReturn( connectionFactory );
        ...
    }
}

我假设在调用构造函数时定义的存根尚未激活。 知道如何使它工作吗?

【问题讨论】:

  • JmsTemplate获取ConnectionFactory并在构造函数中对其进行操作实际上是个坏主意。你不应该像那样得到ConnectionFactory,只需注入它。看起来该类中的构造一开始就有缺陷(事实上,也很难(呃)测试那种指向该方向的点),
  • 无论如何cf.createSession() 行将返回NPE,因为cf 是一个模拟(它是来自测试类的connectionFactory),并且您没有定义它以防万一调用createSession()方法

标签: java spring unit-testing mockito


【解决方案1】:

你的假设是正确的。最好的办法是“手动”创建 bean,而不是让 Spring 自己调用构造函数。为此,将@Configuration 类添加到具有相应@Bean 方法的测试中。


未经测试的示例:

@SpringBootTest
public class MyTestClass {

    @Configuration
    static class MyTestClassConfiguration {
        @Bean
        MyBeanToTest myBeanToTest(jmsTemplate jmsTemplate) {
            // Too bad that this is not within the test…
            when(jmsTemplate.getConnectionFactory())
                    .thenReturn(connectionFactory);
            …
        }
    }
    
}

【讨论】:

    【解决方案2】:

    您必须使用静态存根,以便它存在并在创建上下文之前进行配置。

    另一种更简洁的方法是不要在构造函数中调用任何东西,而是根据需要使用cf.createSession(); 创建会话(它仍然可以是单例的)。

    另一种方法是根本不使用@MockBean,而自己创建GegugProcessingSoapServices

    【讨论】:

      【解决方案3】:

      我建议不要模拟而是存根 JmsTemplate bean。

      @SpringBootTest
      public class MyTestClass {
          @Autowired
          MyBeanToTest myBeanToTest;
      
          @Autowired
          private JmsTemplate jmsTemplate;
          
          @Mock
          private static ConnectionFactory connectionFactory;
      
          @TestConfiguration
          static class Config {
              @Bean
              public JmsTemplate jmsTemplate() {
                  return new JmsTemplate() {
                      public ConnectionFactory getConnectionFactory() {
                          return connectionFactory;
                      }
                  }
              }
          }
      
          @Test
          public void testSomething()  {
              ...
          }
      }
      

      【讨论】:

        【解决方案4】:

        @AutoWired 不适用于被测类。它不会将 bean 带入构造函数。

        解决方案可以使用Mockito @InjectMock,或者自己创建类并在构造函数中使用mock(这通常是一个很好的做法):

        MyBeanToTest myBeanToTest = new MyBeanToTest(connectionFactory)

        【讨论】:

        • @MockBean 就是这样做的,它用模拟实例替换了实际的 bean。玩弄嘲笑而不是使用它只会使事情复杂化。
        • 我的拙见是,模拟的“新”在创建配置方面更加简单和可读。关于@MockBean,你是对的。
        猜你喜欢
        • 2015-12-10
        • 1970-01-01
        • 2011-09-14
        • 2017-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-28
        相关资源
        最近更新 更多