Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #556 improve FxTestUtils to better support testing of FX code. #557

Merged
merged 2 commits into from
Aug 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions mvvmfx-testing-utils/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
<name>mvvmFX testing utils</name>
<description>This module contains some utils for testing</description>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>eu.lestard</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,50 @@
import java.util.concurrent.TimeoutException;

public class FxTestingUtils {


/**
* This method is used to execute some code on the JavaFX Thread.
* It is intended to be used in testing scenarios only because the internal exception
* handling is optimized for testing purposes.
*
* This method is blocking. It will execute the given code runnable and wait
* for it to be finished. However, a timeout of 1000 milliseconds is used.
* If you need another timeout value, use {@link #runInFXThread(Runnable, long)}.
*
* This method will catch exceptions produced by the execution of the code.
* If an {@link AssertionError} is thrown by the code (for example because an test assertion has failed)
* the assertion error will be rethrown by this method so that the JUnit test runner will properly handle it
* and show you the test failure.
* @param code the code to execute on the JavaFX Thread
*/
public static void runInFXThread(Runnable code){
runInFXThread(code, 1000);
}

/**
* See {@link #runInFXThread(Runnable)}. This method takes a timeout of milliseconds
* as second parameter.
* @param code the code to execute on the JavaFX Thread
* @param timeout the timeout to be used before the execution is canceled.
*/
public static void runInFXThread(Runnable code, long timeout){
CompletableFuture<Void> future = new CompletableFuture<>();
CompletableFuture<Object> future = new CompletableFuture<>();

Platform.runLater(() -> {
code.run();
try {
code.run();
} catch (AssertionError e) {
future.complete(e);
return;
}
future.complete(null);
});

try {
future.get(timeout+50, TimeUnit.MILLISECONDS);
Object result = future.get(timeout + 50, TimeUnit.MILLISECONDS);

if(result instanceof AssertionError) {
throw (AssertionError) result;
}
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

/**
* This JUnit 5 extension can be used to start a JavaFX Thread before executing the tests.
* This way a JavaFX Thread will be available in your tests so that you can use <code>Platform.runLater</code>
* and similar constructs in your tests.
*
* However, this extension will not execute your Test method on the JavaFX Thread.
* To execute some test code on the JavaFX Thread you should use {@link FxTestingUtils#runInFXThread(Runnable)}.
*/
public class JfxToolkitExtension implements BeforeAllCallback {
@Override public void beforeAll(ExtensionContext extensionContext) throws Exception {
@Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
new JFXPanel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@
import org.junit.runners.model.InitializationError;

/**
* This basic class runner ensures that JavaFx is running and then wraps all the
* runChild() calls in a Platform.runLater(). runChild() is called for each test
* that is run. By wrapping each call in the Platform.runLater() this ensures
* that the request is executed on the JavaFx thread.
* This JUnit 4 TestRunner can be used to start a JavaFX Thread for test execution.
* This way a JavaFX Thread will be available so that you can use <code>Platform.runLater</code>
* and similar constructs in your test code.
*
* However, this TestRunner alone won't execute your test method on the JavaFX Thread.
* To execute a test method on the JavaFX Thread you have to use the annotation {@link TestInJfxThread}
* together with this test runner.
*
* This solution only works for JUnit 4. For a solution for JUnit 5 please see {@link de.saxsys.mvvmfx.testingutils.JfxToolkitExtension}.
*
*/
public class JfxRunner extends BlockJUnit4ClassRunner {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation can be used together with {@link JfxRunner} to execute
* an annotated test method on the JavaFX Thread.
*
* Please notices that this only works with JUnit 4.
* For a solution for JUnit 5 see {@link de.saxsys.mvvmfx.testingutils.FxTestingUtils#runInFXThread(Runnable)}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TestInJfxThread {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package de.saxsys.mvvmfx.testingutils;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import java.util.concurrent.CompletableFuture;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import javafx.application.Platform;


class Junit5Test {

@ExtendWith(JfxToolkitExtension.class)
@Nested
class OnFxThread {
@Test
void testFxThreadIsAvailable() throws Exception {
assertThat(Platform.isFxApplicationThread()).isFalse();
CompletableFuture<Boolean> isInApplicationThread = new CompletableFuture<>();
Platform.runLater(() -> {
isInApplicationThread.complete(Platform.isFxApplicationThread());
});
assertThat(isInApplicationThread.get()).isTrue();
}


@Test
void testOnFxThreadSuccess () {
FxTestingUtils.runInFXThread(() -> {
assertThat(Platform.isFxApplicationThread()).isTrue();
});
}

@Test
void testOnFxThreadFailed () {
Assertions.assertThrows(AssertionError.class, () -> {
FxTestingUtils.runInFXThread(() -> {
fail("some reason");
});
}, "some reason");
}
}

@Nested
class NotOnFxThread {

@Test
void testFxThreadIsNotAvailable() {
assertThat(Platform.isFxApplicationThread()).isFalse();

Assertions.assertThrows(IllegalStateException.class, () -> {
Platform.runLater(() -> {});
}, "Toolkit not initialized");
}
}
}