将ConfiguartionProperties类导入WebMvcTest会找到配置类,但找不到属性

问题描述 投票:0回答:1

我正在尝试使用 Spring Boot 在 Kotlin 中编写一个简单的 @WebMvcTest。 Test 下的控制器依赖于一个使用 @ConfigurationProperties 注释的 Configuration 类,以从我的 application-*.yml 文件中提取一个简单的字符串属性。我有一个 application-test.yml 文件,它定义了此属性,并使用 @ActiveProfile("test") 注释测试类以使用此属性。因为 @WebMvcTest 是一个切片,默认情况下不会加载我的配置,所以我还添加了 @Import(ApplicationConfiguration::class)。

测试代码现在在尝试构造 ApplicationConfiguration Bean 时失败,因为它找不到该属性。常规应用程序运行良好,并通过 @ConfigurationPropertiesScan 查找属性。

我在发布之前整理了我的代码并更改了一些名称,但以下代码仍然以同样的方式失败。

这是堆栈跟踪的底部:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.vpsxprintproxy.configuration.ApplicationConfiguration': Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:245)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1352)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1189)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:888)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
    ... 96 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1824)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1383)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:888)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
    ... 110 common frames omitted

相关课程:

@WebMvcTest(StatusController::class)
@Import(ApplicationConfiguration::class)
@ActiveProfiles("test")
class StatusControllerTest {

    @Autowired
    private lateinit var mockMvc: MockMvc

    @Test
    fun getStatus() {
        mockMvc.perform(MockMvcRequestBuilders.get("/status"))
            .andExpect(MockMvcResultMatchers.status().isOk)
            .andExpect(
                MockMvcResultMatchers.content().string("Application version someApplicationVersion is up and running.")
            )
    }
}
@RestController
@RequestMapping("/status")
class StatusController(
    private val applicationConfiguration: ApplicationConfiguration
) {
    @GetMapping
    fun getStatus(): String = "Application version ${applicationConfiguration.version} is up and running."
}
@ConfigurationProperties(prefix = "application")
data class ApplicationConfiguration(
    val version: String
)

还有 application-test.yml 文件:

application:
  version: someApplicationVersion

我尝试过的另一件事导致了奇怪的行为:我已经将我的 ApplicationConfiguration 更改为具有像这样的默认值

@ConfigurationProperties(prefix = "application")
data class ApplicationConfiguration(
    var version: String = "default"
)

当我这样做时,测试不仅成功地找到了版本的some值,而且还成功地找到了版本的值。它从 application-test.yml 中找到正确值。

我知道我可以定义自己的测试配置类和/或模拟它来运行这个特定的测试,但这在具有许多属性的类中会很麻烦,我很好奇为什么这种行为存在以及我如何使用我的 application-test.yml 与 @WebMvcTest。我也不想将其设为@SpringBootTest,因为应用程序可能会变得很大,而且我不想启动整个上下文。

有人可以解释这种行为和/或如何使我的 ApplicationConfiguration 实例化 application-test.yml 中的属性吗?

spring-boot kotlin unit-testing testing
1个回答
0
投票

这实际上是切片测试中发生的一个常见问题,并且随着主要 Spring Boot 版本之间的行为变化而加剧。

我认为在某些时候切片测试(例如@WebMvcTest、@DataJpaTest...)确实将@ConfigurationProperties bean导入到切片上下文中,但我找不到任何东西来支持我的主张。

Spring 文档指出:

使用@WebMvcTest注释时,不会扫描常规@Component和@ConfigurationProperties bean。 @EnableConfigurationProperties 可用于包含 @ConfigurationProperties beans。

在你的情况下,你应该以明确的方式导入你的属性bean:

@EnableConfigurationProperties([ApplicationConfiguration::class])
© www.soinside.com 2019 - 2024. All rights reserved.