I am currently working on a simple JavaFX MVC/MVP to test if I could get any unit tests for JavaFX working. My goal is to test a JavaFX dialog or control in a unit test. To make my unit tests as independent as possible from any JavaFX calls I want to try to mock any JavaFX calls. But unfortunately I am struggling to get it working properly. I also don't want to work with TestFX because of the lack of proper documentation.
Until now I am still a bit new to unit testing in Java, but I do have experience in writing unit tests in c++ (google test/mock)
Specs:
- JDK 8
- Mockito 3.5.13
- JUnit 4.12
- Hamcrest 1.3
What do I have until now?
I have created a View class which is responsible for updating the dialog, for registering the actions for a button and for showing the dialog.
public class View {
private Stage stage;
private Label label;
private Button button;
public View(Stage stage, Label label, Button button) {
this.stage = stage;
this.label = label;
this.button = button;
}
public void updateLabel(int count) {
label.setText("Countdown: " + count);
}
public void ShowStage() {
//button.setText("Count");
//stage.setScene(new Scene(new VBox(label, button)));
stage.show();
}
public void RegisterButtonAction(EventHandler<ActionEvent> value) {
button.setOnAction(value);
}
}
Additionally I also have a Unit test for the view, in which I have three tests:
- for the constructor
- for updating a label
- for showing the dialog For this unit test I already have a JavaFX threading rule (see How do you mock a JavaFX toolkit initialization?), which is responsible for starting a JavaFX thread. For the button, the label and the stage I created mocks with mockito, they are injected via the constructor (fast hack for testing). As I created mocks, I expected that the calls to JavaFX are mocked, but it seems that there is still something missing.
public class ViewTest {
@org.junit.Rule
public JavaFXThreadingRule rule = new JavaFXThreadingRule();
private Stage m_stageMock;
private Label m_labelMock;
private Button m_buttonMock;
private View m_testee;
@Before
public void SetUp() throws InterruptedException {
m_stageMock = mock(Stage.class);
m_labelMock = mock(Label.class);
m_buttonMock = mock(Button.class);
m_testee = new View(m_stageMock, m_labelMock, m_buttonMock);
}
@After
public void TearDown(){
m_testee = null;
m_stageMock = null;
m_labelMock = null;
m_buttonMock = null;
}
@Test
public void Constructor_HappyPath_NoCrash() {
}
@Test
public void UpdateLabel_IntegerGiven_PrintNumber() {
int count = 8;
doNothing().when(m_labelMock).setText("Countdown: " + count);
m_testee.updateLabel(count);
}
@Test
public void ShowStage_HappyPath_ShowsWindowWithButtonAndLabel() {
VBox vbox = new VBox();
Scene scene = new Scene(vbox);
//doNothing().when(m_buttonMock).setText("Count");
//doNothing().when(m_stageMock).setScene(any(Scene.class));
doNothing().when(m_stageMock).show();
m_testee.ShowStage();
}
}
What additional things have I tried so far?
- I tried to set mockito in such a way that it is capable to mock final function/classes.
- I tried to implement a custom mock maker based on How do you mock a JavaFX toolkit initialization?.
Maybe someone has a solution or an example for me, or I am just missing something small. I would be very grateful if I could get any help.
All the best
Fossa