Skip to content

Instantly share code, notes, and snippets.

@fappel
Last active August 8, 2023 10:17
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save fappel/65982e5ea7a6b2fde5a3 to your computer and use it in GitHub Desktop.
Save fappel/65982e5ea7a6b2fde5a3 to your computer and use it in GitHub Desktop.
JUnit 4 TestRule to run a test in its own thread
/**
* RunInThread an other accompanying files are licensed under the MIT
* license. Copyright (C) Frank Appel 2016-2021. All rights reserved
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RunInThread {
}
/**
* RunInThread an other accompanying files are licensed under the MIT
* license. Copyright (C) Frank Appel 2016-2021. All rights reserved
*/
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class RunInThreadRule implements TestRule {
@Override
public Statement apply( Statement base, Description description ) {
Statement result = base;
RunInThread annotation = description.getAnnotation( RunInThread.class );
if( annotation != null ) {
result = new RunInThreadStatement( base );
}
return result;
}
}
/**
* RunInThread an other accompanying files are licensed under the MIT
* license. Copyright (C) Frank Appel 2016-2021. All rights reserved
*/
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.runners.model.Statement;
class RunInThreadStatement extends Statement {
private final Statement baseStatement;
private Future<?> future;
private volatile Throwable throwable;
RunInThreadStatement( Statement baseStatement ) {
this.baseStatement = baseStatement;
}
@Override
public void evaluate() throws Throwable {
ExecutorService executorService = runInThread();
try {
waitTillFinished();
} finally {
executorService.shutdown();
}
rethrowAssertionsAndErrors();
}
private ExecutorService runInThread() {
ExecutorService result = Executors.newSingleThreadExecutor();
future = result.submit( new Runnable() {
@Override
public void run() {
try {
baseStatement.evaluate();
} catch( Throwable throwable ) {
RunInThreadStatement.this.throwable = throwable;
}
}
} );
return result;
}
private void waitTillFinished() {
try {
future.get();
} catch( ExecutionException shouldNotHappen ) {
throw new IllegalStateException( shouldNotHappen );
} catch( InterruptedException shouldNotHappen ) {
throw new IllegalStateException( shouldNotHappen );
}
}
private void rethrowAssertionsAndErrors() throws Throwable {
if( throwable != null ) {
throw throwable;
}
}
}
@Gemini-NX
Copy link

Very clear!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment