带有 Kotlin 和 Spring Boot 的 JUnit 5:如何通过依赖注入正确使用自定义测试扩展?

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

我目前正在尝试使用 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 正确使用自定义测试扩展和依赖注入吗?

提前致谢!

spring-boot kotlin dependency-injection junit5
2个回答
0
投票

恐怕你想做的事不可能——至少不可能那样。 原因是 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
- 用于检索注入的值。然后您的扩展可以将测试实例投射到这个接口并从那里获取值。这可能看起来更好一些,但是将扩展的使用限制在实现此接口的测试类上。


0
投票

以编程方式注册扩展似乎可行(我想因为那时 spring 已经注入了值)

所以:

    @RegisterExtension
    @Autowired
    private lateinit var fakeAuthService: FakeAuthService

而不是:

@ExtendWith(FakeAuthService::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MyTest {
© www.soinside.com 2019 - 2024. All rights reserved.