如何在Spring Boot测试中设置“headless”属性?

6

我正在使用基于JavaFX的Spring Boot进行测试,参考了一些优秀的YouTube视频来解决这个问题。

为了使其与TestFX兼容,我需要按照以下方式创建上下文:

@Override
public void init() throws Exception {
    SpringApplicationBuilder builder = new SpringApplicationBuilder(MyJavaFXApplication.class);
    builder.headless(false); // Needed for TestFX
    context = builder.run(getParameters().getRaw().stream().toArray(String[]::new));

    FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
    loader.setControllerFactory(context::getBean);
    rootNode = loader.load();
}

我现在想测试这个JavaFX应用程序,我使用以下方法:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyJavaFXApplicationUITest extends TestFXBase {

    @MockBean
    private MachineService machineService;

    @Test
    public void test() throws InterruptedException {
        WaitForAsyncUtils.waitForFxEvents();
        verifyThat("#statusText", (Text text ) -> text.getText().equals("Machine stopped"));
        clickOn("#startMachineButton");
        verifyThat("#startMachineButton", Node::isDisabled);
        verifyThat("#statusText", (Text text ) -> text.getText().equals("Machine started"));
    }
}

这个操作启动了一个Spring上下文,并按预期用模拟的bean替换了“正常” bean。

然而,现在我得到了一个java.awt.HeadlessException,因为这个“无头”属性没有像正常启动时那样被设置为false。在测试期间如何设置此属性?

编辑:

仔细查看后,似乎有两个上下文被启动,一个是Spring测试框架启动的,另一个是我在init()方法中手动创建的,因此被测试的应用程序没有使用模拟的bean。如果有人知道如何在init()方法中获取测试上下文引用,我会非常高兴。


1
也许这个链接可以帮助你。 - Praveen Kumar K S
2个回答

11

@SpringBootTest 使用 SpringBootContextLoader 类作为上下文加载器,因此 ApplicationContext 是通过方法 SpringBootContextLoader.loadContext 加载的:

SpringApplication application = getSpringApplication();
......
return application.run();

在调用方法 application.run() 时,应用程序会使用其内部无头属性来配置系统的无头属性。

因此,如果我们想在Spring Boot测试中设置 'headless' 属性,只需创建一个自定义特定的 ContextLoader 类,该类继承 SpringBootContextLoader 类,并重写方法 getSpringApplication 并将 headless 属性设置为 false,然后使用注释 @ContextConfiguration 为 @SpringBootTest 赋予特定的 ContextLoader。代码:

public class HeadlessSpringBootContextLoader extends SpringBootContextLoader {
    @Override
    protected SpringApplication getSpringApplication() {
        SpringApplication application = super.getSpringApplication();
        application.setHeadless(false);
        return application;
    }
}

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.NONE)
@ContextConfiguration(loader = HeadlessSpringBootContextLoader.class)
public class ApplicationTests {

    @Test
    public void contextLoads() {

    }

}

4

来自Praveen Kumar的评论指出了正确的方向。当我使用-Djava.awt.headless=false运行测试时,就不会出现异常。

解决2个Spring上下文的问题,我需要执行以下操作:

假设这是您的主JavaFx启动类:

    @SpringBootApplication
    public class MyJavaFXClientApplication extends Application {

    private ConfigurableApplicationContext context;
    private Parent rootNode;

    @Override
    public void init() throws Exception {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(MyJavaFXClientApplication.class);
        builder.headless(false); // Needed for TestFX
        context = builder.run(getParameters().getRaw().stream().toArray(String[]::new));

        FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
        loader.setControllerFactory(context::getBean);
        rootNode = loader.load();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
        double width = visualBounds.getWidth();
        double height = visualBounds.getHeight();

        primaryStage.setScene(new Scene(rootNode, width, height));
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void stop() throws Exception {
        context.close();
    }

    public void setContext(ConfigurableApplicationContext context) {
        this.context = context;
    }
}

为了进行测试,您可以使用此抽象基类(感谢MVP Java的YouTube视频):

public abstract class TestFXBase extends ApplicationTest {

    @BeforeClass
    public static void setupHeadlessMode() {
        if (Boolean.getBoolean("headless")) {
            System.setProperty("testfx.robot", "glass");
            System.setProperty("testfx.headless", "true");
            System.setProperty("prism.order", "sw");
            System.setProperty("prism.text", "t2k");
            System.setProperty("java.awt.headless", "true");
        }
    }

    @After
    public void afterEachTest() throws TimeoutException {
        FxToolkit.hideStage();
        release(new KeyCode[0]);
        release(new MouseButton[0]);
    }

    @SuppressWarnings("unchecked")
    public <T extends Node> T find(String query, Class<T> clazz) {
        return (T) lookup(query).queryAll().iterator().next();
    }
}

然后你可以编写这样的测试:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyJavaFXApplicationUITest extends TestFXBase {

    @MockBean
    private TemperatureService temperatureService;

    @Autowired
    private ConfigurableApplicationContext context;

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
        loader.setControllerFactory(context::getBean);
        Parent rootNode = loader.load();

        stage.setScene(new Scene(rootNode, 800, 600));
        stage.centerOnScreen();
        stage.show();
    }

    @Test
    public void testTemperatureReading() throws InterruptedException {
        when(temperatureService.getCurrentTemperature()).thenReturn(new Temperature(25.0));
        WaitForAsyncUtils.waitForFxEvents();

        assertThat(find("#temperatureText", Text.class).getText()).isEqualTo("25.00 C");
    }
}

这可以使用模拟服务启动用户界面。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接