In JUnit 4, what is the difference between a field annotated `@Rule` and one annotated `@ClassRule` — how must each be declared, and what does each one's wrapped statement cover?
answer
- @Rule = public non-static, per method
- @ClassRule = public static, once per class
- ClassRule wraps @BeforeClass + class body + @AfterClass
- MethodRule cannot be a @ClassRule
- Timeout/ErrorCollector as @ClassRule = undefined
basics
~20 s@Rule is a public non-static field; its statement covers the @Before methods, one test method and the @After methods, so it runs per test. @ClassRule is a public static TestRule field; its statement covers @BeforeClass, the whole class body and @AfterClass, so it runs once.
solid answer
~50 s**`@Rule`** — public, non-static field (or public non-static method returning the rule), typed `TestRule` or the older `MethodRule`. JUnit makes a fresh test instance per method, so the rule object is fresh too, and the statement it wraps is `@Before` methods + test method + `@After` methods. Per-method scope. **`@ClassRule`** — public **static** field (or public static method), typed `TestRule` only. Its statement wraps `@BeforeClass`, the entire class body (or all contained classes when the annotated class is a `Suite`), and `@AfterClass`. It runs once for the class, which is how you share an expensive resource such as a server or container. Two gotchas: `MethodRule` cannot be a class rule because there is no method or instance at class scope; and the statement passed to a `@ClassRule` never throws, so rules that depend on catching test failures — `ErrorCollector`, `ExpectedException`, `Timeout` — have undefined behaviour when used as class rules.
code
java · 16 linespublic class OrderApiTest {
@ClassRule
public static ExternalResource server = new ExternalResource() {
@Override protected void before() { Server.start(); }
@Override protected void after() { Server.stop(); }
};
@Rule
public TemporaryFolder workDir = new TemporaryFolder();
@Test
public void placesOrder() throws Exception {
// server started once for the class, workDir fresh for this test
}
}go deeper
Get the declarations right (public non-static vs public static) and state per-method versus per-class scope.
Add what each wrapped statement contains, the TestRule-only restriction for class rules, and the shared-resource-plus-per-test-reset pattern.
Cover failure semantics when class-rule setup throws, the undefined-behaviour rules, parallel-execution safety, and suite-level class rules.
Reason about the isolation-versus-cost curve across a whole suite: what may be shared per class, per suite, or per JVM, and how that choice interacts with parallelism and flakiness.
## Same mechanism, two scopes Both annotations mark a rule object that decorates a `Statement`. The difference is *which* statement JUnit hands it and *when* the field is read. ### @Rule — per test method - Field: `public`, **not** static; type implements `TestRule` (preferred) or `MethodRule`. A `public`, non-static method returning such an object also works. - Lifecycle: JUnit's default runner instantiates the test class once per test method, so it reads the field from that fresh instance. Every test gets its own rule object; no state leaks from one test to the next. - Scope of the wrapped statement: the `@Before` methods, the test method itself, and the `@After` methods. The rule's setup therefore runs before all `@Before` methods and its cleanup after all `@After` methods. ### @ClassRule — once per class - Field: `public` **and** `static`; type must implement `TestRule`. A `public static` method returning a `TestRule` also works. - Lifecycle: read once, before any instance exists. - Scope of the wrapped statement: `@BeforeClass` methods, the whole body of the class (all its test methods), and `@AfterClass` methods. If the annotated class is a suite (`@RunWith(Suite.class)`), the statement covers every class in that suite — the canonical way to start a server once for a whole suite and stop it at the end. ## Why MethodRule cannot be a class rule `MethodRule.apply(Statement base, FrameworkMethod method, Object target)` needs a reflective method and the test *instance*. At class scope neither exists yet, so only `TestRule` — whose `apply(Statement, Description)` takes only metadata — is supported. This is the main reason JUnit documents `TestRule` as the interface to implement in new code. ## The undefined-behaviour warning JUnit's own documentation states that the statement passed to a `@ClassRule` never throws, and that throwing from a class rule leads to undefined behaviour. Consequently rules built around observing or producing a test failure — `ErrorCollector`, `ExpectedException`, `Timeout` — are documented as having undefined behaviour as class rules. `Timeout` in particular reads plausibly as a class rule ("fail the class if it takes too long") and is a common mistake. `ExternalResource` and `TestWatcher`-style rules are the safe class-rule shapes. ## Choosing between them - Cheap and must be isolated per test (temp folder, fake clock, captured logs) → `@Rule`. - Expensive and safely shareable (database container, embedded server, heavyweight context) → `@ClassRule`, or a `@ClassRule` on a suite class so the cost is paid once for many classes. - Both together is a normal combination: a `@ClassRule` starts the shared database, a `@Rule` truncates or rolls back per test so tests stay independent. The tradeoff is the usual one: class scope buys speed and costs isolation. Anything mutable behind a class rule is shared state, and once tests can run in parallel, shared rule state must be thread-safe. Static rule fields also live for the JVM's lifetime, so leaking resources there shows up as memory growth across a large suite. ## Failure semantics If a class rule's setup throws, the class cannot run: JUnit reports one failure attributed to the class rather than to individual tests, which is a familiar confusing symptom ("one failed test named after the class, zero tests executed"). If a per-test rule's setup throws, only that test fails. ## Inheritance Rule fields are found on superclasses too, so a base class can contribute rules to all subclasses. That is legitimate but reintroduces the inheritance coupling rules exist to avoid — usually better to declare the rule field in each class, or expose it from a small helper that returns a configured rule.
- What happens if the `before()` of a `@ClassRule` throws?No test in the class runs. JUnit reports a failure attached to the class-level description, so the report shows a single failure named after the class and zero executed tests. That is why class-rule setup should fail loudly with a clear message — otherwise engineers see "tests disappeared" rather than "the shared resource could not start".
- Why is `Timeout` documented as undefined behaviour when used as a `@ClassRule`?The statement handed to a class rule is contracted never to throw, and throwing from a class rule is undefined. `Timeout` works by aborting the wrapped statement with an exception, which violates that contract. If you need an overall cap on a class or suite, enforce it outside the test framework rather than with a class-scoped `Timeout` rule.
saying these in an interview costs you the question
- Declaring `@ClassRule` on a non-static field (or `@Rule` on a static one) — both are validation errors.
- Believing a `@ClassRule` runs once per test method.
- Using `MethodRule` as a class rule — only `TestRule` is supported there.
- Assuming a `@ClassRule` gives per-test isolation; anything mutable behind it is shared state across the whole class.
- Saying `@ClassRule` and `@Rule` cannot coexist — combining them (shared resource + per-test reset) is the standard pattern.