如何模拟正在使用 TestFX 测试的控制器中的方法?

问题描述

以下是 TestFX 测试的片段,直接从他们的 GitHub README提取

@ExtendWith(ApplicationExtension.class)
class ClickableButtonTest_junit5Hamcrest {

    private Button button;

    /**
     * Will be called with {@code @Before} semantics,i. e. before each test method.
     *
     * @param stage - Will be injected by the test runner.
     */
    @Start
    private void start(Stage stage) {
        button = new Button("click me!");
        button.setId("myButton");
        button.setonAction(actionEvent -> button.setText("clicked!"));
        stage.setScene(new Scene(new StackPane(button),100,100));
        stage.show();
    }

    /**
     * @param robot - Will be injected by the test runner.
     */
    @Test
    void should_contain_button_with_text(FxRobot robot) {
        FxAssert.verifyThat(button,LabeledMatchers.hasText("click me!"));
        // or (lookup by css id):
        FxAssert.verifyThat("#myButton",LabeledMatchers.hasText("click me!"));
        // or (lookup by css class):
        FxAssert.verifyThat(".button",LabeledMatchers.hasText("click me!"));
    }

我的问题是,特别是改变场景/根源的动作。正在测试的控制器/场景的方面在最后更改了根,这会产生以下堆栈跟踪:

Caused by: java.lang.NullPointerException
    at org.example/org.example.App.setRoot(App.java:67)
    at org.example/org.example.services.AppService.setRoot(AppService.java:20)
    at org.example/org.example.controllers.SecondaryController.switchToGameScreen(SecondaryController.java:64)
    ... 57 more

我对此的解决方案是,如您所见,我为 App 中的静态方法(例如导致 NPE 的 setRoot)创建了一个服务包装类,其中,如果我可以访问控制器,理论上我可以用 Mockito 进行模拟。不幸的是,如果您回到上面的代码示例,似乎没有任何访问控制器类的概念。您获得了与舞台的表面级创建和交互,但我不知道如何访问底层控制器。当然,我需要访问物理控制器才能模拟其服务类。

有谁知道我如何获得对该类的访问权限,以便我可以将其包装类设置为模拟版本?

如果有人想实际使用它,我可以提供源代码

解决方法

想通了。

javafx.fxml.FXMLLoader 有一个名为 getController 的方法。但这并没有那么简单,因为 javafx.fxml.FXMLLoader 必须物理load 到一个 javafx.scene.Parent 对象中才能使控制器存在。

无论如何,这是一个您可以遵循的简短引导程序设置。

@ExtendWith(ApplicationExtension.class)
public class ToTest {
  private Controller controller;

  @Start
  public void setUp(Stage stage) throws IOException {
    FXMLLoader loader = new FXMLLoader(getClass().getResource("totest.fxml"));
    Parent root = loader.load();
    //This must happen AFTER loader.load()
    this.controller = loader.getController();
    stage.setScene(new Scene(root,0));
    stage.show();
  }
}

从那里,您可以对控制器执行任何操作。 (在我的情况下,嘲笑它)