是否可以使用 Spring ApplicationContext 中的 bean 参数化 JUnit Jupiter 测试?

Oli*_*ver 5 spring dependency-injection parameterized junit5

我想编写一个单元测试,它为给定类型的每个 Spring bean 执行。JUnit5 的参数化测试提供了很多可能性,但我不知道如何将 bean 注入方法源,因为它必须是静态方法。

有没有办法根据 Spring 的应用程序上下文来确定 JUnit5 测试的参数?

Sam*_*nen 12

For starters, a factory method configured via @MethodSource does not have to be static. The second sentence in the User Guide explains that.

Factory methods within the test class must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS); whereas, factory methods in external classes must always be static.

Thus, if you use @TestInstance(PER_CLASS) semantics, your @MethodSource factory method can be non-static and can therefore access the ApplicationContext injected into the test instance.

Here's an example that demonstrates that for beans of type String, with an intentional failure for the bar bean.

import java.util.stream.Stream;

import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;

@SpringJUnitConfig
@TestInstance(PER_CLASS)
class SpringBeansParameterizedTests {

    @Autowired
    ApplicationContext applicationContext;

    @ParameterizedTest
    @MethodSource
    void stringBeans(String bean) {
        assertEquals(3, bean.length());
    }

    Stream<String> stringBeans() {
        return applicationContext.getBeansOfType(String.class).values().stream();
    }

    @Configuration
    static class Config {

        @Bean
        String foo() {
            return "foo";
        }

        @Bean
        String bar() {
            return "barf";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

If you don't want to work directly with the ApplicationContext, you can simplify the solution by having the collection of all such beans of a given type (String in this example) injected directly, as follows.

@SpringJUnitConfig
@TestInstance(PER_CLASS)
class SpringBeansParameterizedTests {

    @Autowired
    List<String> stringBeans;

    @ParameterizedTest
    @MethodSource
    void stringBeans(String bean) {
        assertEquals(3, bean.length());
    }

    Stream<String> stringBeans() {
        return this.stringBeans.stream();
    }

    @Configuration
    static class Config {

        @Bean
        String foo() {
            return "foo";
        }

        @Bean
        String bar() {
            return "barf";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)