我目前正在尝试使用 Spring Boot 在 Kotlin 中设置 JUnit 5 测试,并为虚假身份验证服务自定义测试扩展。测试扩展旨在为身份验证服务设置和拆除测试环境,但我遇到了依赖注入问题。
这是我的测试设置:
@ExtendWith(FakeAuthService::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = ["spring.cloud.gcp.secretmanager.enabled=false"])
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class MyTest {
// ...
}
这是我的 FakeAuthService 类:
@Component
class FakeAuthService : BeforeEachCallback, AfterEachCallback {
@Value("\${userendpoint.url}")
private lateinit var url: String
override fun beforeEach(context: ExtensionContext?) {
// set up the test environment
// ...
}
override fun afterEach(context: ExtensionContext?) {
// tear down the test environment
// ...
}
}
我面临的问题是依赖注入在 FakeAuthService 中无法正常工作,并且在尝试访问 url 属性时出现 UninitializedPropertyAccessException。
任何人都可以帮助我了解如何在 JUnit 5 中使用 Kotlin 和 Spring Boot 正确使用自定义测试扩展和依赖注入吗?
提前致谢!
恐怕你想做的事不可能——至少不可能那样。 原因是 Jupiter 扩展的生命周期完全脱离了 Spring 的组件生命周期。因此,扩展不能作为组件,也不能作为价值注入的接收端。
我目前看到一个可行的选择:在您的扩展中使用反射
示例是用 Java 编写的,但您将设法将其翻译成 Kotlin。
import java.lang.reflect.*;
import java.util.*;
import org.junit.jupiter.api.extension.*;
import org.junit.platform.commons.support.*;
class FakeAuthService implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) throws Exception {
Optional<String> optionalUrl = getInjectedField(context, "url", String.class);
optionalUrl.ifPresent(url -> {
// Do whatever you want to do with the url
System.out.println("URL: " + url);
});
}
private static <T> Optional<T> getInjectedField(
ExtensionContext context,
String fieldName,
Class<T> fieldType
) {
Object testInstance = context.getRequiredTestInstance();
Optional<Field> urlField = ReflectionSupport.findFields(
testInstance.getClass(),
field -> field.getName().equals(fieldName),
HierarchyTraversalMode.BOTTOM_UP
).stream().findFirst();
return urlField.map(field -> {
try {
field.setAccessible(true);
return fieldType.cast(field.get(testInstance));
} catch (Throwable e) {
throw new RuntimeException(e);
}
});
}
}
以通用方式检索成员值有点复杂,但您可以为此编写自己的小实用程序。
或者,您的测试类可以实现一个接口 - 例如。
HasURL
- 用于检索注入的值。然后您的扩展可以将测试实例投射到这个接口并从那里获取值。这可能看起来更好一些,但是将扩展的使用限制在实现此接口的测试类上。
以编程方式注册扩展似乎可行(我想因为那时 spring 已经注入了值)
所以:
@RegisterExtension
@Autowired
private lateinit var fakeAuthService: FakeAuthService
而不是:
@ExtendWith(FakeAuthService::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MyTest {