当前位置: 首页 > 知识库问答 >
问题:

如何将自定义bean全局添加到spring启动测试上下文中

翟修永
2023-03-14

我在spring boot应用程序中实现了firebase消息传递。为此,我创建了一个类似本文所述的bean,以便轻松访问FirebaseMessaging实例。然后,该bean被注入到服务中。然后,当调用利用FirebaseMessagingbean的服务方法时,所述服务被注入到我的控制器中。

现在我的问题是该项目的测试套件(使用JUnit5

MyControllerTest > testFunction() FAILED
    java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
        Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
            Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
        Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
                Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
            Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
                Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
                    Caused by: java.lang.IllegalStateException at Preconditions.java:513
                    Caused by: java.lang.IllegalStateException at Preconditions.java:513

根本原因:

Caused by: java.lang.IllegalStateException: FirebaseApp name my-app already exists!
    at com.google.common.base.Preconditions.checkState(Preconditions.java:513)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:222)
    at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:215)
    at my.example.app.firebase.FirebaseMessagingBean.firebaseMessaging(FirebaseMessagingBean.kt:25)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 125 more

注意:这些错误只有在整体运行testsuite时才会出现。如果我自己运行一个测试类,则不会出现错误。

解决这个问题的一种方法是向每个类添加一个@MockBean,这样测试上下文就会加载bean。因此,我可以继续并简单地将其添加到每个测试类中,但我更希望使用一种方法,将FirebaseMessaging@MockBean自动注入到每个测试类中。每当开发人员忘记将@MockBean添加到他的测试类中时,测试套件不会失败。

实现这一目标的正确方法是什么?

(这段代码是静态编程语言,但我也很乐意接受解决方案的java代码。)

代码:

@Component
class FirebaseMessagingBean {
    @Bean
    fun firebaseMessaging(): FirebaseMessaging {
        val privateKeyFileURL = ClassLoader.getSystemResource("firebase-cloud-messaging.json")
            ?: throw IOException("Couldn't read firebase credentials")
        val privateKeyStream = File(privateKeyFileURL.file).inputStream()
        val googleCredentials = GoogleCredentials.fromStream(privateKeyStream)
        val firebaseOptions = FirebaseOptions.builder().setCredentials(googleCredentials)
            .build()
        val app = FirebaseApp.initializeApp(firebaseOptions, "my-app")
        return FirebaseMessaging.getInstance(app)
    }
}
@Service
class FirebaseNotificationService(
    val firebaseMessaging: FirebaseMessaging
) {
    fun sendNotification(msg: String) { /* ... */ }
}
@RestController
@RequestMapping("/api/route")
class MyController(
    val firebaseNotificationService: FirebaseNotificationService,
) {
    @PostMapping()
    fun postRoute(): ResponseEntity<Any> {
        firebaseNotificationService.sendNotification("msg")
        // ...
    }
}

测试类示例

@AutoConfigureMockMvc
@SpringBootTest
class DeviceControllerTest {
    // injections
    
    @Test
    fun testFunction() { /* ... */ }
}

共有1个答案

丌官翰采
2023-03-14

我只在之前没有实例化的情况下初始化FirebasApp实例,从而修复了这个问题。

更新的bean

class FirebaseMessagingBean {
    @Bean
    fun firebaseMessaging(): FirebaseMessaging {
        val privateKeyFileURL = ClassLoader.getSystemResource("firebase-cloud-messaging.json")
            ?: throw IOException("Couldn't read firebase credentials")
        val privateKeyStream = File(privateKeyFileURL.file).inputStream()
        val googleCredentials = GoogleCredentials.fromStream(privateKeyStream)
        val firebaseOptions = FirebaseOptions
            .builder()
            .setCredentials(googleCredentials)
            .build()
        val app = try {
            FirebaseApp.getInstance()
        } catch (iae: IllegalStateException) {
            FirebaseApp.initializeApp(firebaseOptions)
        }
        return FirebaseMessaging.getInstance(app)
    }
}
 类似资料:
  • 问题内容: 我有一些带有自定义注释的类,不应实例化(抽象类,它只是真实bean的子组件)。但是在此类的顶部,在运行时,在上下文初始化阶段,我想将额外的bean放入应用程序上下文中。 因此,基本上,我需要扫描类路径,处理结果,并将新bean引入当前的应用程序上下文中。 似乎spring-mvc,spring-tasks和spring-integration正在这样做(我试图从源中学习它-没运气) 我

  • 我有一些不应该实例化的带有自定义注释的类(抽象类,它只是实际bean的子组件)。但是在这些类之上,在运行时,在上下文初始化阶段,我想在应用程序上下文中添加额外的bean。 因此,基本上我需要扫描类路径,处理结果,并将新bean引入curent应用程序上下文。 似乎是spring-mvc、spring-tasks和spring-integration在做这件事(我试着从源代码中学习--没有运气) 我

  • 我有需要测试的REST服务。该服务具有Spring Security身份验证,我需要在测试或模拟中关闭它。我决定嘲笑它,因为我不能关掉它。我为此编写了,但现在未加载上下文: 在我的主要源代码中,我有一些config类加载了一些其他bean,而it类在我的测试中没有加载,我有一个例外: 我做错了什么?有人能帮我吗?我使用了,但在该版本中,无法工作,因为属性不再存在。

  • 我对Spring Security有这个问题。 我有一个带有SecurityConfig类的java配置实现,它扩展了WebSecurityConfigureAdapter。 在这个类中,我想重写方法“configure()” 一切正常,一切正常。 问题是Spring Context上没有加载“MyDaoAuthenticationProvider”组件。所以我无法注入或自动安装此类中的任何组件:

  • 我在spring TestContext配置的深潜过程中遇到了spring-boot-test问题。 示例项目:github示例项目 我有三个带有bean的Spring配置(-重量级配置,我需要缓存它) 两次然后 只有一次。在我看来,这是可能的,因为已经缓存为TestContext(发生这种情况是因为和包含在同一个包中) 这意味着spring启动我的spring上下文三次!为什么?在日志中查看三次

  • 问题内容: 当您长按Android中的某个按钮时,将显示一个上下文菜单。我想为该系统中的所有菜单添加一些内容。 例如,系统使用“复制和粘贴”来执行此操作。我想添加自己的文件,并将其显示在每个应用程序中。 问题答案: 当前,Android不支持此功能,如果没有特定活动实现您公开的意图或活动,则无法在系统级别全局覆盖或挂钩功能。即使在发布意图的情况下,除非运行的应用程序是使用者也没关系……所有基本系统