JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
Annotations are the primary mechanism in JUnit 4 for marking test methods and controlling test execution lifecycle. They provide a declarative way to define test structure, setup and teardown procedures, test execution rules, and method ordering.
Marks a method as a test method. The test method will be executed by the JUnit runner. Supports optional expected parameter for exception testing and timeout parameter for performance testing.
/**
* Marks a method as a test method
* @param expected - Optional: the exception class that the test is expected to throw
* @param timeout - Optional: timeout in milliseconds, test fails if it runs longer
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
Class<? extends Throwable> expected() default None.class;
long timeout() default 0L;
}Usage Examples:
import org.junit.Test;
import static org.junit.Assert.*;
public class BasicTest {
@Test
public void simpleTest() {
assertEquals(4, 2 + 2);
}
@Test(expected = IllegalArgumentException.class)
public void testException() {
throw new IllegalArgumentException("Expected exception");
}
@Test(timeout = 1000)
public void testTimeout() {
// Test fails if this takes more than 1 second
performQuickOperation();
}
}Marks a method to run before each test method in the class. Use for test setup that needs to be fresh for each test. The method must be public void with no parameters.
/**
* Marks a method to run before each test method
* The annotated method must be public, void, and take no parameters
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {}Usage Examples:
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class ListTest {
private List<String> list;
@Before
public void setUp() {
list = new ArrayList<>();
list.add("initial");
}
@Test
public void testListSize() {
assertEquals(1, list.size());
}
@Test
public void testListAdd() {
list.add("second");
assertEquals(2, list.size());
}
}Marks a method to run after each test method in the class. Use for test cleanup and resource disposal. Runs even if the test or @Before method throws an exception.
/**
* Marks a method to run after each test method
* The annotated method must be public, void, and take no parameters
* Runs even if the test or @Before method fails
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {}Usage Examples:
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class FileTest {
private File tempFile;
@Before
public void createFile() throws IOException {
tempFile = File.createTempFile("test", ".txt");
}
@Test
public void testFileWrite() throws IOException {
FileWriter writer = new FileWriter(tempFile);
writer.write("test data");
writer.close();
assertTrue(tempFile.length() > 0);
}
@After
public void cleanup() {
if (tempFile != null && tempFile.exists()) {
tempFile.delete();
}
}
}Marks a static method to run once before all test methods in the class. Use for expensive setup that can be shared across tests. The method must be public static void with no parameters.
/**
* Marks a static method to run once before all test methods in the class
* The annotated method must be public, static, void, and take no parameters
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeClass {}Usage Examples:
import org.junit.BeforeClass;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
public class DatabaseTest {
private static Connection connection;
@BeforeClass
public static void connectToDatabase() throws Exception {
// Expensive operation done once for all tests
connection = DriverManager.getConnection("jdbc:h2:mem:test");
connection.createStatement().execute("CREATE TABLE users (id INT, name VARCHAR(255))");
}
@Test
public void testInsert() throws Exception {
connection.createStatement().execute("INSERT INTO users VALUES (1, 'Alice')");
}
@Test
public void testQuery() throws Exception {
// Connection is already established
var result = connection.createStatement().executeQuery("SELECT COUNT(*) FROM users");
assertTrue(result.next());
}
}Marks a static method to run once after all test methods in the class. Use for cleanup of resources established in @BeforeClass. Runs even if tests fail.
/**
* Marks a static method to run once after all test methods in the class
* The annotated method must be public, static, void, and take no parameters
* Runs even if tests or @BeforeClass method fail
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterClass {}Usage Examples:
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class ResourceTest {
private static ExpensiveResource resource;
@BeforeClass
public static void setUp() {
resource = new ExpensiveResource();
resource.initialize();
}
@Test
public void testOperation1() {
resource.performOperation();
}
@Test
public void testOperation2() {
resource.performAnotherOperation();
}
@AfterClass
public static void tearDown() {
if (resource != null) {
resource.cleanup();
resource = null;
}
}
}Marks a test method or test class to be ignored (not executed). Useful for temporarily disabling tests without deleting them. Can include an optional message explaining why the test is ignored.
/**
* Marks a test method or test class to be ignored
* @param value - Optional message explaining why the test is ignored
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Ignore {
String value() default "";
}Usage Examples:
import org.junit.Ignore;
import org.junit.Test;
public class FeatureTest {
@Test
public void testWorkingFeature() {
// This test runs normally
assertTrue(true);
}
@Test
@Ignore("Feature not yet implemented")
public void testNewFeature() {
// This test is skipped
implementNewFeature();
}
@Test
@Ignore
public void testBrokenFeature() {
// This test is skipped without a message
brokenFunction();
}
}
// Ignore entire test class
@Ignore("Deprecated functionality, will be removed")
public class LegacyTest {
@Test
public void test1() {
// All tests in this class are ignored
}
@Test
public void test2() {
// This is also ignored
}
}Marks a field or method that returns a TestRule to be applied to each test method. Rules add behavior around test execution such as timeout enforcement, exception handling, or resource management.
/**
* Marks a field or method that returns a TestRule
* The field must be public and of type TestRule or MethodRule
* If a method, it must be public and return TestRule or MethodRule
* @param order - Optional order for rule execution (default -1)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Rule {
int order() default -1;
}Usage Examples:
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.Timeout;
import java.io.File;
import java.io.IOException;
public class RuleTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Rule
public Timeout globalTimeout = Timeout.seconds(10);
@Test
public void testUsingTempFolder() throws IOException {
File file = folder.newFile("test.txt");
assertTrue(file.exists());
// File automatically deleted after test
}
@Test
public void testWithTimeout() {
// This test must complete within 10 seconds
performOperation();
}
}Marks a static field or method that returns a TestRule to be applied once for the entire test class. Useful for expensive setup that should be shared across all tests.
/**
* Marks a static field or method that returns a TestRule for the entire class
* The field must be public static and of type TestRule
* If a method, it must be public static and return TestRule
* @param order - Optional order for rule execution (default -1)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface ClassRule {
int order() default -1;
}Usage Examples:
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.TemporaryFolder;
public class ClassRuleTest {
@ClassRule
public static TemporaryFolder folder = new TemporaryFolder();
@ClassRule
public static ExternalResource resource = new ExternalResource() {
@Override
protected void before() throws Throwable {
// Setup once for all tests
System.out.println("Starting test class");
}
@Override
protected void after() {
// Cleanup once after all tests
System.out.println("Finished test class");
}
};
@Test
public void test1() {
// Both rules are applied
}
@Test
public void test2() {
// Same rule instances used
}
}Specifies the order in which test methods in a class should be executed. By default, JUnit executes methods in an unpredictable order for test isolation.
/**
* Specifies the execution order for test methods in a class
* @param value - The ordering strategy to use
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FixMethodOrder {
MethodSorters value();
}Usage Examples:
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderedTest {
@Test
public void test1_first() {
System.out.println("Runs first (alphabetically)");
}
@Test
public void test2_second() {
System.out.println("Runs second");
}
@Test
public void test3_third() {
System.out.println("Runs third");
}
}
@FixMethodOrder(MethodSorters.JVM)
public class JvmOrderTest {
// Tests run in JVM-returned order (varies by JVM)
@Test
public void testA() {}
@Test
public void testB() {}
}Specifies a custom runner class to execute the tests instead of the default JUnit 4 runner. Essential for using alternative runners like Suite, Parameterized, or Theories.
/**
* Specifies a custom runner class for executing tests
* @param value - The Runner class to use
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface RunWith {
Class<? extends Runner> value();
}Usage Examples:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.junit.runners.Parameterized;
import org.junit.Test;
// Using Suite runner
@RunWith(Suite.class)
@SuiteClasses({TestClass1.class, TestClass2.class, TestClass3.class})
public class AllTests {
// Runs all tests from the specified classes
}
// Using Parameterized runner
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameterized.Parameter(0)
public int input;
@Parameterized.Parameter(1)
public int expected;
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{0, 0}, {1, 1}, {2, 1}, {3, 2}, {4, 3}, {5, 5}
});
}
@Test
public void testFibonacci() {
assertEquals(expected, Fibonacci.compute(input));
}
}Allows for a custom validator to be attached to an annotation. When attached to an annotation, the validator will be instantiated and invoked by the test runner.
/**
* Attaches an AnnotationValidator to an annotation
* @param value - The AnnotationValidator class to use
* @since 4.12
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@Inherited
public @interface ValidateWith {
Class<? extends AnnotationValidator> value();
}
/**
* Validates annotations on classes, methods, and fields.
* Instances must be immutable and thread-safe as they are shared by multiple test runners.
* @since 4.12
*/
public abstract class AnnotationValidator {
/**
* Validates annotation on the given class
* @param testClass - Class being validated
* @return List of validation exceptions (empty if valid)
*/
public List<Exception> validateAnnotatedClass(TestClass testClass);
/**
* Validates annotation on the given field
* @param field - Field being validated
* @return List of validation exceptions (empty if valid)
*/
public List<Exception> validateAnnotatedField(FrameworkField field);
/**
* Validates annotation on the given method
* @param method - Method being validated
* @return List of validation exceptions (empty if valid)
*/
public List<Exception> validateAnnotatedMethod(FrameworkMethod method);
}Usage Examples:
import org.junit.validator.ValidateWith;
import org.junit.validator.AnnotationValidator;
import org.junit.runners.model.TestClass;
import java.lang.annotation.*;
import java.util.ArrayList;
import java.util.List;
// Define custom annotation with validator
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ValidateWith(RequiresJava8Validator.class)
public @interface RequiresJava8 {}
// Implement the validator
public class RequiresJava8Validator extends AnnotationValidator {
@Override
public List<Exception> validateAnnotatedClass(TestClass testClass) {
List<Exception> errors = new ArrayList<>();
String javaVersion = System.getProperty("java.version");
if (!javaVersion.startsWith("1.8") && !javaVersion.startsWith("8")) {
errors.add(new Exception(
"Test requires Java 8 but running on " + javaVersion));
}
return errors;
}
}
// Use the validated annotation
@RequiresJava8
public class Java8OnlyTest {
@Test
public void testStreamApi() {
// Test will only run on Java 8+
}
}Specifies a custom ordering for test methods. Allows fine-grained control over test execution order beyond the basic alphabetical or JVM ordering.
/**
* Specifies an ordering for test methods
* @param value - The Ordering.Factory class to use
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@ValidateWith(OrderWithValidator.class)
public @interface OrderWith {
Class<? extends Ordering.Factory> value();
}Usage Examples:
import org.junit.runner.OrderWith;
import org.junit.runner.manipulation.Alphanumeric;
import org.junit.Test;
@OrderWith(Alphanumeric.class)
public class CustomOrderedTest {
@Test
public void testA() {
// Tests run in alphanumeric order
System.out.println("Test A");
}
@Test
public void testB() {
System.out.println("Test B");
}
}/**
* Method ordering strategies for @FixMethodOrder
*/
public enum MethodSorters {
/**
* Sorts methods by method name, in lexicographic order
*/
NAME_ASCENDING,
/**
* Leaves test methods in order returned by JVM (may vary)
*/
JVM,
/**
* Default - pseudo-random order that changes between runs
*/
DEFAULT
}
/**
* Placeholder class for @Test annotation's default expected value
*/
public class None extends Throwable {
private None() {}
}Install with Tessl CLI
npx tessl i tessl/maven-junit--junit