在 JUnit 5 中正确设置(系统)属性

Properly set (system) properties in JUnit 5

我们在 JUnit 4 测试中使用类似于 System Rules 的方法来处理(系统)属性。这样做的主要原因是为了在每次测试后清理环境,以免其他测试无意中依赖可能产生的副作用。

自从 JUnit 5 发布以来,我想知道是否有 "JUnit 5 way" 这样做?

可以使用extension API。您可以创建一个注释来定义您对测试方法的扩展。

import org.junit.jupiter.api.extension.ExtendWith;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtendWith(SystemPropertyExtension.class)
public @interface SystemProperty {

    String key();

    String value();
}

然后,您可以创建扩展 class:

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class SystemPropertyExtension implements AfterEachCallback, BeforeEachCallback {

    @Override
    public void afterEach(ExtensionContext extensionContext) throws Exception {
        SystemProperty annotation = extensionContext.getTestMethod().get().getAnnotation(SystemProperty.class);
        System.clearProperty(annotation.key());
    }

    @Override
    public void beforeEach(ExtensionContext extensionContext) throws Exception {
        SystemProperty annotation = extensionContext.getTestMethod().get().getAnnotation(SystemProperty.class);
        System.setProperty(annotation.key(), annotation.value());
    }
}

最后,您可以使用属性注释您的测试:

@Test
@SystemProperty(key = "key", value = "value")
void testPropertey() {
    System.out.println(System.getProperty("key"));
}

此解决方案每个测试仅支持一个系统 属性。如果你想支持多重测试,你可以使用嵌套注释,扩展也可以处理这个问题:

@Test
@SystemProperties({
    @SystemProperty(key = "key1", value = "value"),
    @SystemProperty(key = "key2", value = "value")
})
void testPropertey() {
    System.out.println(System.getProperty("key1"));
    System.out.println(System.getProperty("key2"));
}

JUnit Pioneer, a "JUnit 5 extension pack". It comes with @ClearSystemProperty and @SetSystemProperty. From the docs:

The @ClearSystemProperty and @SetSystemProperty annotations can be used to clear, respectively, set the values of system properties for a test execution. Both annotations work on the test method and class level, are repeatable as well as combinable. After the annotated method has been executed, the properties mentioned in the annotation will be restored to their original value or will be cleared if they didn't have one before. Other system properties that are changed during the test, are not restored.

示例:

@Test
@ClearSystemProperty(key = "some key")
@SetSystemProperty(key = "another key", value = "new value")
void test() {
    assertNull(System.getProperty("some key"));
    assertEquals("new value", System.getProperty("another key"));
}

JUnit Pioneer 方式要求系统属性在编译时已知。在运行时生成它们的地方,比如通过 TestcontainersWiremock 在随机端口上创建东西,最好使用可以从动态值驱动的东西。

问题可以通过系统存根 https://github.com/webcompere/system-stubs 解决,它提供 JUnit 5,是系统 Lambda 代码的分支,它本身由系统规则的作者构建。

@ExtendWith(SystemStubsExtension.class)
class SomeTest {
    // can be initialised here with some up front properties
    // or leave like this for auto initialization
    @SystemStub
    private SystemProperties someProperties;

    @BeforeEach
    void beforeEach() {
        someProperties.set("prop1", "value1")
            .set("prop2", "value2");
    }

    @Test
    void someTest() {
        // properties are set here
        // and can also call System.setProperty


        // properties reset to state before the test case ran
        // as the test case is tidied up
    }
}