我的情况:
我正在构建一个使用Kotlin和SpringBoot 2.0.3的应用程序。我正在尝试在JUnit5中编写所有单元测试。所有这三个对我来说都是新手,所以我有点挣扎。
我正在使用@ConfigurationProperties类(而不是@Value)将来自application.yml的值注入到Spring上下文中。
@Configuration
@ConfigurationProperties(prefix = "amazon.aws.s3")
class AmazonS3Config {
val s3Enabled: Boolean = false
val region: String = ""
val accessKeyId: String = ""
val secretAccessKey: String = ""
val bucketName: String = ""
}
然后按照Kotlin / Spring的最佳实践,我将创建一个利用这些属性的Kotlin类,以将注入的类定义为构造函数参数。
class VqsS3FileReader(val amazonS3Config: AmazonS3Config) : VqsFileReader {
companion object: mu.KLogging()
override fun getInputStream(filePath: String): InputStream {
val region: String = amazonS3Config.region
val accessKeyId: String = amazonS3Config.accessKeyId
val secretAccessKey: String = amazonS3Config.secretAccessKey
val bucketName: String = amazonS3Config.bucketName
logger.debug { "The configured s3Enabled is: $s3Enabled" }
logger.debug { "The configured region is: $region" }
logger.debug { "The configured accessKeyId is: $accessKeyId" }
logger.debug { "The configured secretAccessKey is: $secretAccessKey" }
logger.debug { "The configured bucketName is: $bucketName" }
val file: File? = File(filePath)
//This method is not yet implemented, just read a file from local disk for now
return file?.inputStream() ?: throw FileNotFoundException("File at $filePath is null")
}
}
我尚未完成此实现,因为我试图先使单元测试正常工作。因此,目前,此方法实际上并没有扩展到S3,仅流化了本地文件。
我的单元测试就是卡住的地方。我不知道如何将我的application.yml中的属性注入到测试上下文中。由于ConfigProperty类作为构造参数传递,因此在单元测试中建立服务时必须传递它。我尝试了各种无效的解决方案。我找到了这条信息,这很有帮助:
如果使用Spring Boot,则可以使用@ConfigurationProperties而不是@Value批注,但是当前这仅适用于lateinit或可为null的var属性(建议使用前者),因为尚不支持由构造函数初始化的不可变类。
所以这意味着我不能使用类VqsS3FileReaderTest(amazonS3Config:AmazonS3Config):TestBase(){...},然后将配置传递给我的服务。
这是我目前拥有的:
@ActiveProfiles("test")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(SpringExtension::class)
@ContextConfiguration(classes = [AmazonS3Config::class, VqsS3FileReader::class])
class VqsS3FileReaderTest(): TestBase() {
@Autowired
private lateinit var amazonS3Config: AmazonS3Config
@Autowired
private lateinit var fileReader: VqsS3FileReader
val filePath: String = "/fileio/sampleLocalFile.txt"
@Test
fun `can get input stream from a valid file path` () {
fileReader = VqsS3FileReader(amazonS3Config)
val sampleLocalFile: File? = getFile(filePath) //getFile is defined in the TestBase class, it just gets a file in my "resources" dir
if (sampleLocalFile != null) {
val inStream: InputStream = fileReader.getInputStream(sampleLocalFile.absolutePath)
val content: String = inStream.readBytes().toString(Charset.defaultCharset())
assert.that(content, startsWith("Lorem Ipsum"))
} else {
fail { "The file at $filePath was not found." }
}
}
}
这样,我的测试运行了,我的上下文似乎正确设置了,但是我的application.yml中的属性没有被注入。对于我的调试输出,我看到以下内容:
08:46:43.111 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured s3Enabled is: false
08:46:43.111 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured region is:
08:46:43.112 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured accessKeyId is:
08:46:43.112 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured secretAccessKey is:
08:46:43.112 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured bucketName is:
所有空字符串,这是默认值。不是我的application.yml中的值:
amazon.aws.s3:
s3Enabled: true
region: us-west-2
accessKeyId: unknown-at-this-time
secretAccessKey: unknown-at-this-time
bucketName: test-bucket
答案 0 :(得分:0)
我在以下行中看到错误:
@ContextConfiguration(classes = [AmazonS3Config::class, VqsS3FileReader::class])
请在这里放置 configuration 类(而不只是bean)。
简短-易于修复测试
在主模块(例如在具有生产代码的模块中)中创建类(如果缺少),例如VqsS3Configration
在与测试相同的程序包中创建VqsS3TestConfigration之类的类。此文件上的内容:
@org.springframework.context.annotation.Configuration // mark, that this is configuration class
@org.springframework.context.annotation.Import(VqsS3Configration::class) // it references production configuration from test configuration
@org.springframework.context.annotation.ComponentScan // ask Spring to autoload all files from the package with VqsS3TestConfigration and all child packages
class VqsS3TestConfigration {
/*put test-related beans here in future*/
}
然后去测试并更改声明:
@ContextConfiguration(classes = [VqsS3TestConfigration ::class]) // we ask Spring to load configuration here
我在这里创建了示例应用程序:https://github.com/imanushin/spring-boot2-junit5-and-kotlin-integration
请在src文件夹中执行行.\gradlew.bat test
或gradlew.bat bootRun
。测试将检查,我们能够读取属性。 bootRun将打印自动加载的属性
无聊的理论
首先-Spring具有Configuration类-加载和初始化其他类需要它们。配置类的主要目的是代替服务类或共性类,而只需创建服务,组件等。
如果我们简化Spring应用程序负载的算法,则将是这样的:
3.1。对于使用@ConfigurationProperties注释的类,请在此处放置配置项
3.2。对于使用@RestController注释的类-将其注册为Rest控制器
3.N。等等...
Spring如何理解,应该加载什么配置?
@ContextConfiguration
属性@Import
批注,然后获取子代,然后检查其导入等)因此,在我们的例子中,Spring将执行以下操作:
答案 1 :(得分:0)
好的,这花了我整整一整天,但最终我将应用程序属性加载到了单元测试上下文中。我进行了2次更改:
首先,我将@Service批注添加到我最初忘记的VqsS3FileReader服务中。此外,虽然我已更新测试以不通过构造函数注入AmazonS3Config,但我却忽略了更新服务以执行相同操作。所以我改变了
此:
class VqsS3FileReader(val amazonS3Config: AmazonS3Config) : VqsFileReader {
companion object: mu.KLogging()
...
对此:
@Service
class VqsS3FileReader : VqsFileReader {
companion object: mu.KLogging()
@Resource
private lateinit var amazonS3Config: AmazonS3Config
...
最后,我在测试中修改了Spring注释。
从这里:
@ActiveProfiles("test")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(SpringExtension::class)
@ContextConfiguration(classes = [AmazonS3Config::class, VqsS3FileReader::class])
class VqsS3FileReaderTest(): TestBase() {
...
对此:
@ActiveProfiles("test")
@SpringBootTest
@ComponentScan("com.ilmn.*")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(SpringExtension::class)
@EnableAutoConfiguration
@SpringJUnitConfig(SpringBootContextLoader::class)
class VqsS3FileReaderTest(): TestBase() {
...
现在我的测试中似乎有很多注解...因此,我将仔细研究它们各自的实际作用,并查看是否可以减少它。但是至少现在我的属性已注入到我的测试上下文中。