0

The main goal is to run code when the scene is loaded and I know Initializable is the recommended solution and I've been using it except the problem I'm running into is that it's not conducive to integration testing.

The logic in my initialize method is dependent on previous scenes, so when I load up just this one screen, I get some NullPointer exceptions because some things I expected to exist do not exist.

There is no way for me to mock any of this because, using TestFX, the initialize method is run on the first line of setup, FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));.

From there, I would have done loader.getController(); to get access to the generated controller and mock certain class fields and stuff, but the issue is that I can't do this before the initialize method runs. It's a catch 22 situation because to get access to the controller javafx.fxml.FXMLLoader must run, but when that runs it automatically executes the initialize method, which I would otherwise need to mock parts of, or generate expected information.

So, what are my options? Is there a way to run code at the start of a scene without Initializable?

public class GameScreenController implements Initializable {

    private AppService appService;

    private PlayerService playerService;

    private DirectionService directionService;

    private RoomDirectionService roomDirectionService;

    @FXML
    private javafx.scene.control.Button closeButton;

    @FXML
    private Label goldAmount;

    @FXML
    private ImageView player;

    private final BooleanProperty wPressed = new SimpleBooleanProperty(false);
    private final BooleanProperty aPressed = new SimpleBooleanProperty(false);
    private final BooleanProperty sPressed = new SimpleBooleanProperty(false);
    private final BooleanProperty dPressed = new SimpleBooleanProperty(false);

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        this.appService = new AppService();
        this.directionService = new DirectionService();
        this.roomDirectionService = new RoomDirectionService(this.directionService);
        this.goldAmount.setText(String.valueOf(this.appService.getPlayerState().getGoldAmount()));
        this.playerService = new PlayerService(this.player, this.appService,
                this.roomDirectionService);
        this.playerService.moveX(this.appService.getPlayerState().getSpawnCoordinates()[0]);
        this.playerService.moveY(this.appService.getPlayerState().getSpawnCoordinates()[1]);
...
notacorn
  • 3,526
  • 4
  • 30
  • 60
  • Use can set a controller factory on the `FXMLLoader` to customize the creation of the controller. Create the controller yourself, mock what you need, return it from the factory, and then simply let the `FXMLLoader` continue from there. Though perhaps you should provide a [mre] demonstrating what's null when the controller is initialized, as it sounds like there may be an issue with the design. – Slaw Mar 14 '21 at 06:45
  • that sounds promising, could you provide an example or link to one? I'm out of my depth when it comes to the things youre talking about @Slaw – notacorn Mar 14 '21 at 06:47
  • @Slaw updated with the relevant code portions – notacorn Mar 14 '21 at 06:50
  • the NPE comes at the first time we invoke appservice to get the player state, because the previous controller was responsible for initializing the player state – notacorn Mar 14 '21 at 06:51
  • How does the previous controller initialize the player state if this controller creates its own separate services? – Slaw Mar 14 '21 at 06:55
  • there are static attributes being referenced by the app service, which are written to by the previous controller through the app service as a conduit – notacorn Mar 14 '21 at 06:56
  • 2
    Ah. Unless there's no other way you should avoid static state. One of the issues with static state is that it's hard to test. If you need to share model instances between controllers then I would design the controllers to allow passing them said model instances. You'd use the `initialize` method to complete any UI setup that's independent of the model data. Then when you'd have something like a `setAppService(AppService)` method that you call on the controller instance you get from the FXML loader. That method would set the instance but also trigger any further initialization needed. – Slaw Mar 14 '21 at 07:11
  • 2
    That setup would allow you to mock the model as much as needed and gives you more control over how the system behaves. – Slaw Mar 14 '21 at 07:12
  • how can you avoid static state? if you need to remember information between scenes, the only other thing i can think of is just throw in a mongorepository – notacorn Mar 14 '21 at 07:14
  • 1
    also, what do you think about this answer? with the factory you were talking about https://stackoverflow.com/a/66190527/8652920 – notacorn Mar 14 '21 at 07:25
  • 1
    That answer gives a small example what I was talking about. Depending on how much work you've put into your project that may be the easiest fix. But you should look into the concept of "dependency injection", whether that's accomplished manually or via a framework. The basic idea is you pass in the dependencies needed by an object instance. If you design your application right then you can ensure all objects share the data they need without resorting to static state (at least for the most part). – Slaw Mar 14 '21 at 08:45
  • i think this has been a good discussion. since I'm not working alone I am not able to just switch to spring boot (totally understand the advantages of DI) I ended up, for every scene change, manually creating the fxml loaders and using the controller factory pattern so subsequent controllers would be constructed with a custom constructor. – notacorn Mar 14 '21 at 09:08
  • Does this answer your question? [JavaFX : Pass parameters while instantiating controller class](https://stackoverflow.com/questions/30814258/javafx-pass-parameters-while-instantiating-controller-class) – gkhaos Mar 15 '21 at 09:52
  • technically, that doesn't answer my original question of `Initializable` alternatives. not sure what to do about that – notacorn Mar 15 '21 at 16:48

0 Answers0