在 Groovy 测试期间 Spring Autowire bean NullPointerException 但在运行时工作正常

Posted

技术标签:

【中文标题】在 Groovy 测试期间 Spring Autowire bean NullPointerException 但在运行时工作正常【英文标题】:Spring Autowire bean NullPointerException during Groovy tests but works fine during Runtime 【发布时间】:2021-09-06 10:16:53 【问题描述】:

我正在使用 prometheus 库来获取我的 Spring Boot 应用程序 (REST API) 的指标。我正在使用库 io.prometheus.simpleclient:0.4.0 并将它包含在我的 Maven pom.xml 中。我正在使用Counter 和@Autowiring(我已经尝试过字段和构造函数注入)它到我自己的类之一,就像这样

MyCustomMetricsClass.java

@Component
public MyCustomMetricsClass 
    @Autowire
    private Counter counterBean;

   public void myOwnMetricsMethod() 
       counterBean.inc();
       // do some stuff
    

那么,我正在 @Autowiring 这个MyCustomMetricsClass 到我的服务类 MyServiceClass.java 中,当我使用 Spring 在本地运行我的 API 时它似乎运行良好在端口 8080 (localhost:8080) 上启动嵌入式 tomcat。我可以点击端点,并且在执行器端点(localhost:8080/actuator/metrics)上正确报告了指标。例如

MyServiceClass.java

public MyServiceClass 
    @Autowire
    private MyCustomMetricsclass myMetrics;


    public void genericServiceMethod() 
        myMetrics.MyOwnMetricsMethod(); // NULL POINTER EXCEPTION ONLY DURING TEST SCOPE (GROOVY)
    

问题是,当我运行mvn install(触发我编写的本地 Groovy 单元测试)时,我不断收到 NULL POINTER EXCEPTION。使用调试器,我可以调试 Groovy 单元测试,并在我的 Service 类中看到 myMetrics 变量为 NULL。但是我不明白为什么它在运行时可以正常工作,而且我已经将MyCustomMetricsClass 注释为 @Component 注释,所以它应该是一个被 Spring Component scan 扫描的 bean。

这是一个多模块项目;结构如下

my-project (root, contains root pom.xml)
   - my-api (module, contains RestController. has its own pom.xml)
   - my-service (module. contains service classes, has its own pom.xml)
   - my-model (module, contains all POJO/DTO model classes, has its own pom.xml)

我是否缺少对类路径的依赖?为什么它在运行时有效,但在测试期间无效? (我所有的依赖项都应该有默认范围)自动装配被破坏了吗?

【问题讨论】:

【参考方案1】:

你能分享你的单元测试代码吗?

猜测一下,您使用的是一个模拟框架,也许是 Mockito?

如果这个假设成立,请记住,您的单元测试不会运行完整的 Spring 上下文,因此不会发生自动装配。您需要为自动装配的组件注入模拟。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceClassTest 

  @Mock
  private MyCustomMetricsClass myCustomMetricsClass; 

  @InjectMocks
  private MyServiceClass myServiceClass;

  @Test
  public void shouldDoTesting() 

    myServiceClass. genericServiceMethod();

    verify(myMetrics).MyOwnMetricsMethod();

  

【讨论】:

谢谢。我正在使用 Groovy/Spock。你是对的,我没有嘲笑我的服务类中使用的上述“myMetrics”变量,但由于某种原因,它仍然没有被正确地嘲笑我嘲笑它就像:def myCustomMetricsMock = Mock(MyCustomMetricsClass.class)跨度> 谢谢,我在 Groovy 中嘲笑它,但仍然得到 NPE。我没有意识到在 groovy 中,或者至少在我们的代码库中,我们需要在 Test 下模拟整个类,然后将所有模拟设置为引用该类的所有成员变量/字段。当我这样做时,NPE 消失了,一切都编译好了。我会给你最好的答案,因为你指出了我正确的方向,谢谢!

以上是关于在 Groovy 测试期间 Spring Autowire bean NullPointerException 但在运行时工作正常的主要内容,如果未能解决你的问题,请参考以下文章

groovy脚本实现对spring bean的任意调用

在Spring Boot项目中使用Spock测试框架

Spring 在测试期间从包私有类获取配置

在单元测试期间填充 Spring @Value

Spring Boot:@TestConfiguration 在集成测试期间不覆盖 Bean

在 Citrus TestNG 测试期间 Spring Boot 未加载测试特定应用程序属性