JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
JUnit 4 provides several built-in runners for common testing scenarios. The standard runner executes regular test classes, while specialized runners enable test suites and parameterized tests.
The standard JUnit 4 test runner. Processes test classes with JUnit 4 annotations (@Test, @Before, @After, etc.) and executes them according to the JUnit 4 lifecycle.
/**
* Standard JUnit 4 test class runner
* This is the default runner used when no @RunWith annotation is present
*/
public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
/**
* Creates a runner for the given test class
* @param testClass - Class containing tests
* @throws InitializationError if the test class is malformed
*/
public BlockJUnit4ClassRunner(Class<?> testClass) throws InitializationError;
/**
* Returns the methods that run tests
* @return List of test methods
*/
protected List<FrameworkMethod> computeTestMethods();
/**
* Creates test instance
* @return New instance of test class
*/
protected Object createTest() throws Exception;
/**
* Validates that test class is correctly formed
* @param errors - Collects validation errors
*/
protected void collectInitializationErrors(List<Throwable> errors);
/**
* Returns Statement that executes test method
* @param method - Test method
* @return Statement for execution
*/
protected Statement methodBlock(FrameworkMethod method);
/**
* Returns Statement that invokes test method
* @param method - Test method
* @param test - Test instance
* @return Statement that invokes method
*/
protected Statement methodInvoker(FrameworkMethod method, Object test);
/**
* Returns rules that apply to the test
* @param target - Test instance
* @return List of TestRule objects
*/
protected List<TestRule> getTestRules(Object target);
/**
* Returns class rules
* @return List of TestRule objects for the class
*/
protected List<TestRule> classRules();
}Usage Examples:
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
// Explicit use (normally not needed, this is the default)
@RunWith(BlockJUnit4ClassRunner.class)
public class StandardTest {
private Calculator calculator;
@Before
public void setUp() {
calculator = new Calculator();
}
@Test
public void testAddition() {
assertEquals(5, calculator.add(2, 3));
}
@Test
public void testSubtraction() {
assertEquals(1, calculator.subtract(3, 2));
}
@After
public void tearDown() {
calculator = null;
}
}
// Custom runner extending BlockJUnit4ClassRunner
public class TimingRunner extends BlockJUnit4ClassRunner {
public TimingRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test) {
Statement statement = super.methodInvoker(method, test);
return new Statement() {
@Override
public void evaluate() throws Throwable {
long start = System.currentTimeMillis();
try {
statement.evaluate();
} finally {
long duration = System.currentTimeMillis() - start;
System.out.println(method.getName() + " took " + duration + "ms");
}
}
};
}
}Runs multiple test classes together as a suite. Useful for organizing tests and running related test classes as a group.
/**
* Runner for test suites
* Executes multiple test classes together
*/
public class Suite extends ParentRunner<Runner> {
/**
* Creates a suite for the given class
* @param klass - Suite class annotated with @SuiteClasses
* @param builders - Runner builders
* @throws InitializationError if suite is malformed
*/
public Suite(Class<?> klass, RunnerBuilder builder) throws InitializationError;
/**
* Creates a suite with specific test classes
* @param klass - Suite class
* @param suiteClasses - Classes to include in suite
* @throws InitializationError if suite is malformed
*/
public Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError;
/**
* Creates a suite with specific runners
* @param klass - Suite class
* @param runners - Runners to include
* @throws InitializationError if suite is malformed
*/
public Suite(Class<?> klass, List<Runner> runners) throws InitializationError;
/**
* Creates an empty suite
* @return Empty suite runner
*/
public static Runner emptySuite();
/**
* Annotation for specifying classes in a suite
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SuiteClasses {
Class<?>[] value();
}
}Usage Examples:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
// Basic suite
@RunWith(Suite.class)
@SuiteClasses({
TestClass1.class,
TestClass2.class,
TestClass3.class
})
public class AllTests {
// Empty class - just holds annotations
}
// Nested suites
@RunWith(Suite.class)
@SuiteClasses({
UnitTests.class,
IntegrationTests.class
})
public class AllProjectTests {
}
@RunWith(Suite.class)
@SuiteClasses({
CalculatorTest.class,
StringUtilsTest.class,
DateUtilsTest.class
})
public class UnitTests {
}
@RunWith(Suite.class)
@SuiteClasses({
DatabaseTest.class,
NetworkTest.class
})
public class IntegrationTests {
}
// Running the suite programmatically
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
public class TestRunner {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(AllTests.class);
System.out.println("Tests run: " + result.getRunCount());
System.out.println("Failures: " + result.getFailureCount());
}
}Runs the same test multiple times with different parameters. Each parameter set creates a separate test instance.
/**
* Runner for parameterized tests
* Runs tests multiple times with different parameter values
*/
public class Parameterized extends Suite {
/**
* Creates parameterized test runner
* @param klass - Test class
* @throws Throwable if initialization fails
*/
public Parameterized(Class<?> klass) throws Throwable;
/**
* Annotation for parameter data method
* Method must be static and return Collection<Object[]> or Iterable<Object[]>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Parameters {
/**
* Optional name pattern for test iterations
* Use {index} for iteration number, {0}, {1}, etc. for parameter values
*/
String name() default "{index}";
}
/**
* Annotation for parameter field (not constructor parameter)
* Indicates which parameter from the array should be injected
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Parameter {
/**
* Index in parameter array (default 0)
*/
int value() default 0;
}
/**
* Annotation for methods that run before each parameter set
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeParam {}
/**
* Annotation for methods that run after each parameter set
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterParam {}
/**
* Use custom factory for creating test runners
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UseParametersRunnerFactory {
Class<? extends ParametersRunnerFactory> value();
}
}Usage Examples:
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.junit.Test;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
// Field injection style
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameter(0)
public int input;
@Parameter(1)
public int expected;
@Parameters(name = "fib({0}) = {1}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{0, 0},
{1, 1},
{2, 1},
{3, 2},
{4, 3},
{5, 5},
{6, 8}
});
}
@Test
public void testFibonacci() {
assertEquals(expected, Fibonacci.compute(input));
}
}
// Constructor injection style
@RunWith(Parameterized.class)
public class AdditionTest {
private int a;
private int b;
private int sum;
public AdditionTest(int a, int b, int sum) {
this.a = a;
this.b = b;
this.sum = sum;
}
@Parameters(name = "{0} + {1} = {2}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{1, 1, 2},
{2, 3, 5},
{5, 7, 12},
{10, 20, 30}
});
}
@Test
public void testAddition() {
assertEquals(sum, a + b);
}
}
// Using Iterable instead of Collection
@RunWith(Parameterized.class)
public class PrimeTest {
@Parameter
public int number;
@Parameters(name = "isPrime({0})")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] {
{2}, {3}, {5}, {7}, {11}, {13}
});
}
@Test
public void testPrime() {
assertTrue("Should be prime: " + number, isPrime(number));
}
}
// Complex parameter objects
@RunWith(Parameterized.class)
public class UserValidationTest {
@Parameter(0)
public User user;
@Parameter(1)
public boolean expectedValid;
@Parameters(name = "{0} is valid: {1}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{new User("Alice", 25), true},
{new User("Bob", -1), false},
{new User("", 30), false},
{new User(null, 20), false}
});
}
@Test
public void testValidation() {
assertEquals(expectedValid, user.isValid());
}
}Explicit JUnit 4 runner. Functionally identical to BlockJUnit4ClassRunner but with a simpler name.
/**
* Alias for BlockJUnit4ClassRunner with a shorter name
*/
public class JUnit4 extends BlockJUnit4ClassRunner {
/**
* Creates a JUnit 4 runner
* @param klass - Test class
* @throws InitializationError if test class is malformed
*/
public JUnit4(Class<?> klass) throws InitializationError;
}Usage Examples:
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.junit.Test;
@RunWith(JUnit4.class)
public class SimpleTest {
@Test
public void testSomething() {
assertTrue(true);
}
}Runner for JUnit 3.x compatibility. Allows running legacy JUnit 3 test suites.
/**
* Runner for JUnit 3.x style test suites
* Provides backward compatibility with JUnit 3
*/
public class AllTests extends SuiteMethod {
/**
* Creates runner for JUnit 3 style suite
* @param klass - Class with static suite() method
* @throws Throwable if initialization fails
*/
public AllTests(Class<?> klass) throws Throwable;
}Usage Examples:
import org.junit.runner.RunWith;
import org.junit.runners.AllTests;
import junit.framework.Test;
import junit.framework.TestSuite;
@RunWith(AllTests.class)
public class LegacyTests {
public static Test suite() {
TestSuite suite = new TestSuite("Legacy Test Suite");
suite.addTestSuite(OldStyleTest1.class);
suite.addTestSuite(OldStyleTest2.class);
return suite;
}
}Runs all inner classes of a test class as separate test classes. Useful for organizing related tests in nested classes.
/**
* Runner that executes all inner classes as tests
* Each inner class is run with its own runner
*/
public class Enclosed extends Suite {
/**
* Creates enclosed runner
* @param klass - Outer test class
* @param builder - Runner builder
* @throws Throwable if initialization fails
*/
public Enclosed(Class<?> klass, RunnerBuilder builder) throws Throwable;
}Usage Examples:
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.Test;
import static org.junit.Assert.*;
@RunWith(Enclosed.class)
public class CalculatorTests {
// Each inner class runs as separate test
public static class AdditionTests {
@Test
public void testPositiveNumbers() {
assertEquals(5, 2 + 3);
}
@Test
public void testNegativeNumbers() {
assertEquals(-5, -2 + -3);
}
}
public static class SubtractionTests {
@Test
public void testPositiveNumbers() {
assertEquals(1, 3 - 2);
}
@Test
public void testNegativeNumbers() {
assertEquals(-1, -3 - -2);
}
}
public static class MultiplicationTests {
@Test
public void testPositiveNumbers() {
assertEquals(6, 2 * 3);
}
}
}/**
* Base class for runners that have children
*/
public abstract class ParentRunner<T> extends Runner {
protected ParentRunner(Class<?> testClass) throws InitializationError;
/**
* Get children to run
* @return List of children
*/
protected abstract List<T> getChildren();
/**
* Get description for a child
* @param child - Child to describe
* @return Description
*/
protected abstract Description describeChild(T child);
/**
* Run a child
* @param child - Child to run
* @param notifier - Notifier for events
*/
protected abstract void runChild(T child, RunNotifier notifier);
/**
* Returns Statement for running all children
* @param notifier - Notifier for events
* @return Statement
*/
protected Statement childrenInvoker(RunNotifier notifier);
}
/**
* Factory for creating test runners
*/
public interface ParametersRunnerFactory {
/**
* Create runner for parameterized test
* @param testWithParameters - Test parameters
* @return Runner for the test
*/
Runner createRunnerForTestWithParameters(TestWithParameters testWithParameters) throws InitializationError;
}
/**
* Default factory for creating parameterized test runners
*/
public class BlockJUnit4ClassRunnerWithParametersFactory implements ParametersRunnerFactory {
@Override
public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError;
}
/**
* BlockJUnit4ClassRunner for a single parameter set
*/
public class BlockJUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunner {
public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test) throws InitializationError;
}
/**
* Container for parameterized test data
*/
public class TestWithParameters {
public TestWithParameters(String name, TestClass testClass, List<Object> parameters);
public String getName();
public TestClass getTestClass();
public List<Object> getParameters();
}
/**
* Wraps a test class with metadata
*/
public class TestClass {
/**
* Creates a TestClass wrapping the given class
* Scans the class for annotations (expensive operation - share instances when possible)
* @param clazz - The class to wrap
* @throws IllegalArgumentException if class has more than one constructor
*/
public TestClass(Class<?> clazz);
/**
* Returns the underlying Java class
* @return The wrapped class
*/
public Class<?> getJavaClass();
/**
* Returns the class's name
* @return The class name
*/
public String getName();
/**
* Returns the only public constructor in the class
* @return The single public constructor
* @throws AssertionError if there are more or less than one
*/
public Constructor<?> getOnlyConstructor();
/**
* Returns all non-overridden methods that are annotated
* @return List of all annotated methods in the class hierarchy
* @since 4.12
*/
public List<FrameworkMethod> getAnnotatedMethods();
/**
* Returns all non-overridden methods annotated with the specified annotation
* @param annotationClass - The annotation class to search for
* @return List of methods with the annotation
*/
public List<FrameworkMethod> getAnnotatedMethods(Class<? extends Annotation> annotationClass);
/**
* Returns all non-overridden fields that are annotated
* @return List of all annotated fields in the class hierarchy
* @since 4.12
*/
public List<FrameworkField> getAnnotatedFields();
/**
* Returns all non-overridden fields annotated with the specified annotation
* @param annotationClass - The annotation class to search for
* @return List of fields with the annotation
*/
public List<FrameworkField> getAnnotatedFields(Class<? extends Annotation> annotationClass);
/**
* Gets values of annotated fields on the test instance
* @param test - Test instance
* @param annotationClass - Annotation to search for
* @param valueClass - Expected type of field values
* @return List of field values
*/
public <T> List<T> getAnnotatedFieldValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass);
/**
* Finds annotated fields with the specified type, retrieves values and passes to consumer
* @param test - Test instance
* @param annotationClass - Annotation to search for
* @param valueClass - Expected type of field values
* @param consumer - Consumer to receive field values
* @since 4.13
*/
public <T> void collectAnnotatedFieldValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass,
MemberValueConsumer<T> consumer);
/**
* Gets return values of annotated methods on the test instance
* @param test - Test instance
* @param annotationClass - Annotation to search for
* @param valueClass - Expected return type
* @return List of method return values
*/
public <T> List<T> getAnnotatedMethodValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass);
/**
* Finds annotated methods with the specified return type, invokes them and passes results to consumer
* @param test - Test instance
* @param annotationClass - Annotation to search for
* @param valueClass - Expected return type
* @param consumer - Consumer to receive method return values
* @since 4.13
*/
public <T> void collectAnnotatedMethodValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass,
MemberValueConsumer<T> consumer);
/**
* Returns whether the class is public
* @return true if the class has public modifier
*/
public boolean isPublic();
/**
* Returns whether the class is a non-static inner class
* @return true if the class is a member class and not static
*/
public boolean isANonStaticInnerClass();
/**
* Returns the annotations on this class
* @return Array of annotations
*/
public Annotation[] getAnnotations();
/**
* Returns the annotation of the specified type on this class
* @param annotationClass - Annotation type to search for
* @return The annotation, or null if not present
*/
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
}
/**
* Consumer interface for collecting annotated member values
* @since 4.13
*/
public interface MemberValueConsumer<T> {
/**
* Accepts a member and its value
* @param member - The framework member (field or method)
* @param value - The value from the member
*/
void accept(FrameworkMember<?> member, T value);
}
/**
* Represents a method on a test class
*/
public class FrameworkMethod {
/**
* Creates a FrameworkMethod for the given method
* @param method - The method to wrap
* @throws NullPointerException if method is null
*/
public FrameworkMethod(Method method);
/**
* Returns the underlying Java method
* @return The wrapped method
*/
public Method getMethod();
/**
* Returns the method's name
* @return The method name
*/
public String getName();
/**
* Invokes this method on target with parameters
* InvocationTargetExceptions are unwrapped and their causes rethrown
* @param target - Object to invoke method on
* @param params - Method parameters
* @return The method's return value
* @throws Throwable if the method throws an exception
*/
public Object invokeExplosively(Object target, Object... params) throws Throwable;
/**
* Returns the return type of the method
* @return The method's return type
*/
public Class<?> getReturnType();
/**
* Returns the return type of the method (alias for getReturnType)
* @return The method's return type
*/
public Class<?> getType();
/**
* Returns the class where the method is actually declared
* @return The declaring class
*/
public Class<?> getDeclaringClass();
/**
* Validates that the method is public, void, has no arguments
* Adds errors to the list if validation fails
* @param isStatic - Whether method should be static
* @param errors - List to collect validation errors
*/
public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors);
/**
* Validates that the method is public, void
* Adds errors to the list if validation fails
* @param isStatic - Whether method should be static
* @param errors - List to collect validation errors
*/
public void validatePublicVoid(boolean isStatic, List<Throwable> errors);
/**
* Validates that method arguments have no type parameters
* Adds errors to the list if validation fails
* @param errors - List to collect validation errors
*/
public void validateNoTypeParametersOnArgs(List<Throwable> errors);
/**
* Returns the annotations on this method
* @return Array of annotations
*/
public Annotation[] getAnnotations();
/**
* Returns the annotation of the specified type on this method
* @param annotationClass - Annotation type to search for
* @return The annotation, or null if not present
*/
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* Returns true if this is a no-arg method that returns a value assignable to type
* @param type - The type to check
* @return true if method produces the specified type
* @deprecated Used only by Theories runner, will be replaced
*/
@Deprecated
public boolean producesType(Type type);
}
/**
* Represents a field on a test class
*/
public class FrameworkField {
public FrameworkField(Field field);
public Field getField();
public String getName();
public Object get(Object target) throws IllegalAccessException;
public Class<?> getType();
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
}
/**
* Exception thrown during runner initialization
*/
public class InitializationError extends Exception {
public InitializationError(List<Throwable> errors);
public InitializationError(Throwable error);
public InitializationError(String message);
public List<Throwable> getCauses();
}Install with Tessl CLI
npx tessl i tessl/maven-junit--junit