0
# Rules
1
2
Rules provide a way to add reusable behavior around test execution. They can be applied to individual test methods (`@Rule`) or entire test classes (`@ClassRule`). Rules are more flexible and composable than inheritance-based approaches.
3
4
## Capabilities
5
6
### TestRule Interface
7
8
The core interface for implementing custom rules. Rules wrap test execution with custom behavior.
9
10
```java { .api }
11
/**
12
* Interface for test rules
13
* A rule wraps test execution with custom behavior
14
*/
15
public interface TestRule {
16
/**
17
* Modifies the Statement that executes a test
18
* @param base - The Statement to be modified
19
* @param description - Description of the test
20
* @return Modified Statement
21
*/
22
Statement apply(Statement base, Description description);
23
}
24
```
25
26
**Usage Examples:**
27
28
```java
29
import org.junit.rules.TestRule;
30
import org.junit.runner.Description;
31
import org.junit.runners.model.Statement;
32
33
public class LoggingRule implements TestRule {
34
@Override
35
public Statement apply(Statement base, Description description) {
36
return new Statement() {
37
@Override
38
public void evaluate() throws Throwable {
39
System.out.println("Starting: " + description.getMethodName());
40
try {
41
base.evaluate();
42
System.out.println("Passed: " + description.getMethodName());
43
} catch (Throwable t) {
44
System.out.println("Failed: " + description.getMethodName());
45
throw t;
46
}
47
}
48
};
49
}
50
}
51
52
// Usage
53
public class MyTest {
54
@Rule
55
public TestRule loggingRule = new LoggingRule();
56
57
@Test
58
public void testSomething() {
59
// Logging happens automatically
60
assertTrue(true);
61
}
62
}
63
```
64
65
### TemporaryFolder
66
67
Creates temporary files and folders that are automatically deleted after the test. Useful for tests that need file system access.
68
69
```java { .api }
70
/**
71
* Rule for creating temporary files and folders
72
* Automatically deleted after test completion
73
*/
74
public class TemporaryFolder extends ExternalResource {
75
/**
76
* Creates a TemporaryFolder using default temp directory
77
*/
78
public TemporaryFolder();
79
80
/**
81
* Creates a TemporaryFolder in specified parent folder
82
* @param parentFolder - Parent directory for temp folder
83
*/
84
public TemporaryFolder(File parentFolder);
85
86
/**
87
* Get the root temporary folder
88
* @return Root folder (created lazily)
89
*/
90
public File getRoot();
91
92
/**
93
* Create a new temporary file with random name
94
* @return Created file
95
* @throws IOException if file cannot be created
96
*/
97
public File newFile() throws IOException;
98
99
/**
100
* Create a new temporary file with specified name
101
* @param filename - Name for the file
102
* @return Created file
103
* @throws IOException if file cannot be created
104
*/
105
public File newFile(String filename) throws IOException;
106
107
/**
108
* Create a new temporary folder with random name
109
* @return Created folder
110
* @throws IOException if folder cannot be created
111
*/
112
public File newFolder() throws IOException;
113
114
/**
115
* Create a new temporary folder with specified name
116
* @param folderName - Name for the folder
117
* @return Created folder
118
* @throws IOException if folder cannot be created
119
*/
120
public File newFolder(String folderName) throws IOException;
121
122
/**
123
* Create a new temporary folder with path segments
124
* @param folderNames - Path segments for nested folders
125
* @return Created folder
126
* @throws IOException if folder cannot be created
127
*/
128
public File newFolder(String... folderNames) throws IOException;
129
130
/**
131
* Delete all files and folders
132
*/
133
public void delete();
134
135
/**
136
* Builder for configuring TemporaryFolder
137
*/
138
public static class Builder {
139
public Builder parentFolder(File parentFolder);
140
public Builder assureDeletion();
141
public TemporaryFolder build();
142
}
143
144
public static Builder builder();
145
}
146
```
147
148
**Usage Examples:**
149
150
```java
151
import org.junit.Rule;
152
import org.junit.Test;
153
import org.junit.rules.TemporaryFolder;
154
import java.io.*;
155
import static org.junit.Assert.*;
156
157
public class FileTest {
158
@Rule
159
public TemporaryFolder folder = new TemporaryFolder();
160
161
@Test
162
public void testFileCreation() throws IOException {
163
File file = folder.newFile("test.txt");
164
assertTrue(file.exists());
165
// File automatically deleted after test
166
}
167
168
@Test
169
public void testFileWrite() throws IOException {
170
File file = folder.newFile("data.txt");
171
FileWriter writer = new FileWriter(file);
172
writer.write("Hello, World!");
173
writer.close();
174
175
BufferedReader reader = new BufferedReader(new FileReader(file));
176
assertEquals("Hello, World!", reader.readLine());
177
reader.close();
178
}
179
180
@Test
181
public void testFolderCreation() throws IOException {
182
File subFolder = folder.newFolder("subfolder");
183
File nestedFile = new File(subFolder, "nested.txt");
184
nestedFile.createNewFile();
185
assertTrue(nestedFile.exists());
186
}
187
188
@Test
189
public void testNestedFolders() throws IOException {
190
File nested = folder.newFolder("level1", "level2", "level3");
191
assertTrue(nested.exists());
192
assertTrue(nested.isDirectory());
193
}
194
}
195
196
// Using builder
197
public class ConfiguredFolderTest {
198
@Rule
199
public TemporaryFolder folder = TemporaryFolder.builder()
200
.parentFolder(new File("/tmp"))
201
.assureDeletion()
202
.build();
203
}
204
```
205
206
### ExpectedException
207
208
**DEPRECATED since JUnit 4.13** - Use `Assert.assertThrows()` instead.
209
210
Verifies that code throws expected exceptions with specific properties. More flexible than `@Test(expected=...)`.
211
212
```java { .api }
213
/**
214
* Rule for verifying expected exceptions
215
* Provides detailed exception matching
216
*
217
* @deprecated Since 4.13 - Use Assert.assertThrows(Class, ThrowingRunnable) instead
218
*/
219
@Deprecated
220
public class ExpectedException implements TestRule {
221
/**
222
* Create an ExpectedException rule
223
* @return New ExpectedException instance
224
* @deprecated Since 4.13 - Use Assert.assertThrows() instead
225
*/
226
@Deprecated
227
public static ExpectedException none();
228
229
/**
230
* Expect exception of specific type
231
* @param type - Expected exception class
232
*/
233
public void expect(Class<? extends Throwable> type);
234
235
/**
236
* Expect exception matching a matcher
237
* @param matcher - Hamcrest matcher for exception
238
*/
239
public void expect(Matcher<?> matcher);
240
241
/**
242
* Expect exception message containing specific text
243
* @param substring - Expected substring in message
244
*/
245
public void expectMessage(String substring);
246
247
/**
248
* Expect exception message matching a matcher
249
* @param matcher - Hamcrest matcher for message
250
*/
251
public void expectMessage(Matcher<String> matcher);
252
253
/**
254
* Expect exception cause matching a matcher
255
* @param expectedCause - Matcher for cause
256
*/
257
public void expectCause(Matcher<? extends Throwable> expectedCause);
258
259
/**
260
* Check if any exception is expected
261
* @return true if exception expected
262
*/
263
public boolean isAnyExceptionExpected();
264
265
/**
266
* Configure to handle AssertionErrors as exceptions
267
* @return This instance for chaining
268
*/
269
public ExpectedException handleAssertionErrors();
270
271
/**
272
* Configure to handle AssumptionViolatedExceptions
273
* @return This instance for chaining
274
*/
275
public ExpectedException handleAssumptionViolatedExceptions();
276
277
/**
278
* Configure custom message for missing exception
279
* @param message - Custom message
280
* @return This instance for chaining
281
*/
282
public ExpectedException reportMissingExceptionWithMessage(String message);
283
}
284
```
285
286
**Usage Examples:**
287
288
```java
289
import org.junit.Rule;
290
import org.junit.Test;
291
import org.junit.rules.ExpectedException;
292
import static org.hamcrest.CoreMatchers.*;
293
294
public class ExceptionTest {
295
@Rule
296
public ExpectedException thrown = ExpectedException.none();
297
298
@Test
299
public void testExceptionType() {
300
thrown.expect(IllegalArgumentException.class);
301
throw new IllegalArgumentException("Invalid argument");
302
}
303
304
@Test
305
public void testExceptionMessage() {
306
thrown.expect(NullPointerException.class);
307
thrown.expectMessage("Cannot be null");
308
processNull(null);
309
}
310
311
@Test
312
public void testExceptionMessagePattern() {
313
thrown.expect(IllegalStateException.class);
314
thrown.expectMessage(containsString("invalid state"));
315
performInvalidOperation();
316
}
317
318
@Test
319
public void testExceptionCause() {
320
thrown.expect(RuntimeException.class);
321
thrown.expectCause(isA(IOException.class));
322
throw new RuntimeException("Wrapped", new IOException("IO error"));
323
}
324
325
@Test
326
public void testMultipleConditions() {
327
thrown.expect(IllegalArgumentException.class);
328
thrown.expectMessage(startsWith("Invalid"));
329
thrown.expectMessage(containsString("parameter"));
330
validateParameter(-1);
331
}
332
}
333
```
334
335
### Timeout
336
337
Applies a timeout to all test methods in a class. Test fails if it runs longer than specified time.
338
339
```java { .api }
340
/**
341
* Rule for enforcing test timeouts
342
*/
343
public class Timeout implements TestRule {
344
/**
345
* Create timeout in milliseconds
346
* @param millis - Timeout in milliseconds
347
* @return Timeout rule
348
*/
349
public static Timeout millis(long millis);
350
351
/**
352
* Create timeout in seconds
353
* @param seconds - Timeout in seconds
354
* @return Timeout rule
355
*/
356
public static Timeout seconds(long seconds);
357
358
/**
359
* Get builder for configuring timeout
360
* @return Builder instance
361
*/
362
public static Builder builder();
363
364
/**
365
* Builder for Timeout configuration
366
*/
367
public static class Builder {
368
/**
369
* Set timeout duration
370
* @param timeout - Timeout value
371
* @param unit - Time unit
372
* @return This builder
373
*/
374
public Builder withTimeout(long timeout, TimeUnit unit);
375
376
/**
377
* Enable looking for stuck threads
378
* @param enable - Whether to look for stuck threads
379
* @return This builder
380
*/
381
public Builder withLookingForStuckThread(boolean enable);
382
383
/**
384
* Build the Timeout rule
385
* @return Configured Timeout
386
*/
387
public Timeout build();
388
}
389
}
390
```
391
392
**Usage Examples:**
393
394
```java
395
import org.junit.Rule;
396
import org.junit.Test;
397
import org.junit.rules.Timeout;
398
import java.util.concurrent.TimeUnit;
399
400
public class TimeoutTest {
401
@Rule
402
public Timeout globalTimeout = Timeout.seconds(10);
403
404
@Test
405
public void testFastOperation() {
406
// Must complete within 10 seconds
407
performQuickOperation();
408
}
409
410
@Test
411
public void testAnotherOperation() {
412
// Also must complete within 10 seconds
413
performOperation();
414
}
415
}
416
417
// Using builder
418
public class ConfiguredTimeoutTest {
419
@Rule
420
public Timeout timeout = Timeout.builder()
421
.withTimeout(5, TimeUnit.SECONDS)
422
.withLookingForStuckThread(true)
423
.build();
424
425
@Test
426
public void testWithStuckThreadDetection() {
427
performOperation();
428
}
429
}
430
431
// Milliseconds timeout
432
public class MillisTimeoutTest {
433
@Rule
434
public Timeout timeout = Timeout.millis(500);
435
436
@Test
437
public void testVeryFastOperation() {
438
// Must complete within 500ms
439
quickOperation();
440
}
441
}
442
```
443
444
### TestName
445
446
Provides access to the name of the currently executing test method. Useful for debugging and logging.
447
448
```java { .api }
449
/**
450
* Rule for accessing current test method name
451
*/
452
public class TestName extends TestWatcher {
453
/**
454
* Get the name of the current test method
455
* @return Test method name
456
*/
457
public String getMethodName();
458
}
459
```
460
461
**Usage Examples:**
462
463
```java
464
import org.junit.Rule;
465
import org.junit.Test;
466
import org.junit.rules.TestName;
467
import static org.junit.Assert.*;
468
469
public class NamedTest {
470
@Rule
471
public TestName testName = new TestName();
472
473
@Test
474
public void testA() {
475
assertEquals("testA", testName.getMethodName());
476
System.out.println("Running: " + testName.getMethodName());
477
}
478
479
@Test
480
public void testB() {
481
assertEquals("testB", testName.getMethodName());
482
}
483
484
@Test
485
public void testWithLogging() {
486
String currentTest = testName.getMethodName();
487
log("Starting test: " + currentTest);
488
performOperation();
489
log("Finished test: " + currentTest);
490
}
491
}
492
```
493
494
### ErrorCollector
495
496
Collects multiple errors in a single test, allowing the test to continue after failures. Useful for validating multiple conditions.
497
498
```java { .api }
499
/**
500
* Rule for collecting multiple errors
501
* Test continues after failures and reports all errors at end
502
*/
503
public class ErrorCollector extends Verifier {
504
/**
505
* Add an error
506
* @param error - Throwable to collect
507
*/
508
public void addError(Throwable error);
509
510
/**
511
* Check a condition and collect error if false
512
* @param value - Value to check
513
* @param matcher - Matcher to apply
514
*/
515
public <T> void checkThat(T value, Matcher<T> matcher);
516
517
/**
518
* Check a condition with reason
519
* @param reason - Reason for the check
520
* @param value - Value to check
521
* @param matcher - Matcher to apply
522
*/
523
public <T> void checkThat(String reason, T value, Matcher<T> matcher);
524
525
/**
526
* Call a callable and collect errors
527
* @param callable - Code to execute
528
* @return Result of callable
529
*/
530
public <T> T checkSucceeds(Callable<T> callable);
531
532
/**
533
* Check that runnable throws expected exception
534
* @param expectedThrowable - Expected exception type
535
* @param runnable - Code to execute
536
*/
537
public <T extends Throwable> void checkThrows(
538
Class<T> expectedThrowable,
539
ThrowingRunnable runnable
540
);
541
}
542
```
543
544
**Usage Examples:**
545
546
```java
547
import org.junit.Rule;
548
import org.junit.Test;
549
import org.junit.rules.ErrorCollector;
550
import static org.hamcrest.CoreMatchers.*;
551
552
public class MultipleChecksTest {
553
@Rule
554
public ErrorCollector collector = new ErrorCollector();
555
556
@Test
557
public void testMultipleValues() {
558
// All checks are performed even if some fail
559
collector.checkThat("First check", 1 + 1, is(2));
560
collector.checkThat("Second check", 2 + 2, is(4));
561
collector.checkThat("Third check", 3 + 3, is(6));
562
// If any fail, all failures are reported together
563
}
564
565
@Test
566
public void testTableData() {
567
int[][] table = getTestData();
568
for (int i = 0; i < table.length; i++) {
569
for (int j = 0; j < table[i].length; j++) {
570
collector.checkThat(
571
"Cell [" + i + "][" + j + "]",
572
table[i][j],
573
greaterThan(0)
574
);
575
}
576
}
577
// Reports all failing cells, not just the first one
578
}
579
580
@Test
581
public void testWithCallable() {
582
String result = collector.checkSucceeds(() -> {
583
return performOperation();
584
});
585
assertNotNull(result);
586
}
587
}
588
```
589
590
### ExternalResource
591
592
Base class for rules that set up and tear down external resources. Template for resource management.
593
594
```java { .api }
595
/**
596
* Base class for rules that manage external resources
597
*/
598
public abstract class ExternalResource implements TestRule {
599
/**
600
* Override to set up resource before test
601
*/
602
protected void before() throws Throwable;
603
604
/**
605
* Override to tear down resource after test
606
*/
607
protected void after();
608
}
609
```
610
611
**Usage Examples:**
612
613
```java
614
import org.junit.rules.ExternalResource;
615
import org.junit.Rule;
616
import org.junit.Test;
617
618
public class DatabaseResource extends ExternalResource {
619
private Database database;
620
621
@Override
622
protected void before() throws Throwable {
623
database = new Database();
624
database.connect();
625
database.initialize();
626
}
627
628
@Override
629
protected void after() {
630
if (database != null) {
631
database.disconnect();
632
}
633
}
634
635
public Database getDatabase() {
636
return database;
637
}
638
}
639
640
public class DatabaseTest {
641
@Rule
642
public DatabaseResource dbResource = new DatabaseResource();
643
644
@Test
645
public void testQuery() {
646
Database db = dbResource.getDatabase();
647
Result result = db.query("SELECT * FROM users");
648
assertNotNull(result);
649
}
650
}
651
652
// Server resource example
653
public class ServerResource extends ExternalResource {
654
private TestServer server;
655
private int port;
656
657
public ServerResource(int port) {
658
this.port = port;
659
}
660
661
@Override
662
protected void before() throws Throwable {
663
server = new TestServer(port);
664
server.start();
665
}
666
667
@Override
668
protected void after() {
669
if (server != null) {
670
server.stop();
671
}
672
}
673
674
public String getUrl() {
675
return "http://localhost:" + port;
676
}
677
}
678
```
679
680
### RuleChain
681
682
Chains multiple rules to execute in a specific order. Useful when rules have dependencies.
683
684
```java { .api }
685
/**
686
* Chains multiple rules to execute in order
687
*/
688
public class RuleChain implements TestRule {
689
/**
690
* Create an empty rule chain
691
* @return Empty RuleChain
692
*/
693
public static RuleChain emptyRuleChain();
694
695
/**
696
* Create a rule chain starting with a rule
697
* @param outerRule - First rule in chain
698
* @return RuleChain with the rule
699
*/
700
public static RuleChain outerRule(TestRule outerRule);
701
702
/**
703
* Add a rule to the chain
704
* @param rule - Rule to add
705
* @return This RuleChain
706
*/
707
public RuleChain around(TestRule rule);
708
}
709
```
710
711
**Usage Examples:**
712
713
```java
714
import org.junit.Rule;
715
import org.junit.Test;
716
import org.junit.rules.RuleChain;
717
import org.junit.rules.ExternalResource;
718
import org.junit.rules.TestRule;
719
720
public class ChainedRulesTest {
721
private DatabaseResource database = new DatabaseResource();
722
private ServerResource server = new ServerResource(8080);
723
724
@Rule
725
public TestRule chain = RuleChain
726
.outerRule(database) // First: set up database
727
.around(server); // Then: start server (can use database)
728
729
@Test
730
public void testWithBothResources() {
731
// Both database and server are available
732
makeRequestToServer();
733
}
734
}
735
736
// More complex chain
737
public class ComplexChainTest {
738
@Rule
739
public TestRule chain = RuleChain
740
.outerRule(new LoggingRule())
741
.around(new TimeoutRule())
742
.around(new ResourceRule());
743
744
@Test
745
public void test() {
746
// Rules execute in order: Logging -> Timeout -> Resource
747
// Cleanup happens in reverse: Resource -> Timeout -> Logging
748
}
749
}
750
```
751
752
### TestWatcher
753
754
Observes test execution and provides hooks for test lifecycle events. Base class for monitoring rules.
755
756
```java { .api }
757
/**
758
* Base class for rules that observe test execution
759
*/
760
public abstract class TestWatcher implements TestRule {
761
/**
762
* Called when test succeeds
763
* @param description - Test description
764
*/
765
protected void succeeded(Description description);
766
767
/**
768
* Called when test fails
769
* @param e - Throwable that caused failure
770
* @param description - Test description
771
*/
772
protected void failed(Throwable e, Description description);
773
774
/**
775
* Called when test is skipped
776
* @param e - AssumptionViolatedException
777
* @param description - Test description
778
*/
779
protected void skipped(AssumptionViolatedException e, Description description);
780
781
/**
782
* Called when test is about to start
783
* @param description - Test description
784
*/
785
protected void starting(Description description);
786
787
/**
788
* Called when test finishes (success or failure)
789
* @param description - Test description
790
*/
791
protected void finished(Description description);
792
}
793
```
794
795
**Usage Examples:**
796
797
```java
798
import org.junit.rules.TestWatcher;
799
import org.junit.runner.Description;
800
import org.junit.Rule;
801
import org.junit.Test;
802
803
public class DetailedWatcher extends TestWatcher {
804
@Override
805
protected void starting(Description description) {
806
System.out.println("Starting: " + description.getMethodName());
807
}
808
809
@Override
810
protected void succeeded(Description description) {
811
System.out.println("PASSED: " + description.getMethodName());
812
}
813
814
@Override
815
protected void failed(Throwable e, Description description) {
816
System.out.println("FAILED: " + description.getMethodName());
817
System.out.println("Error: " + e.getMessage());
818
}
819
820
@Override
821
protected void finished(Description description) {
822
System.out.println("Finished: " + description.getMethodName());
823
}
824
}
825
826
public class WatchedTest {
827
@Rule
828
public TestWatcher watcher = new DetailedWatcher();
829
830
@Test
831
public void testSomething() {
832
assertTrue(true);
833
}
834
}
835
836
// Screenshot on failure example
837
public class ScreenshotOnFailure extends TestWatcher {
838
@Override
839
protected void failed(Throwable e, Description description) {
840
String testName = description.getMethodName();
841
takeScreenshot(testName + "_failure.png");
842
}
843
844
private void takeScreenshot(String filename) {
845
// Screenshot logic
846
}
847
}
848
```
849
850
### Stopwatch
851
852
Measures and logs test execution time. Provides hooks for accessing timing information.
853
854
```java { .api }
855
/**
856
* Rule for measuring test execution time
857
*/
858
public class Stopwatch implements TestRule {
859
/**
860
* Get runtime in milliseconds
861
* @param unit - Time unit to convert to
862
* @return Runtime in specified unit
863
*/
864
public long runtime(TimeUnit unit);
865
866
/**
867
* Called when test succeeds
868
* @param nanos - Execution time in nanoseconds
869
* @param description - Test description
870
*/
871
protected void succeeded(long nanos, Description description);
872
873
/**
874
* Called when test fails
875
* @param nanos - Execution time in nanoseconds
876
* @param e - Throwable that caused failure
877
* @param description - Test description
878
*/
879
protected void failed(long nanos, Throwable e, Description description);
880
881
/**
882
* Called when test is skipped
883
* @param nanos - Time until skip
884
* @param e - AssumptionViolatedException
885
* @param description - Test description
886
*/
887
protected void skipped(long nanos, AssumptionViolatedException e, Description description);
888
889
/**
890
* Called when test finishes
891
* @param nanos - Execution time in nanoseconds
892
* @param description - Test description
893
*/
894
protected void finished(long nanos, Description description);
895
}
896
```
897
898
**Usage Examples:**
899
900
```java
901
import org.junit.Rule;
902
import org.junit.Test;
903
import org.junit.rules.Stopwatch;
904
import org.junit.runner.Description;
905
import java.util.concurrent.TimeUnit;
906
907
public class TimingTest {
908
@Rule
909
public Stopwatch stopwatch = new Stopwatch() {
910
@Override
911
protected void succeeded(long nanos, Description description) {
912
long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
913
System.out.println(description.getMethodName() + " succeeded in " + millis + "ms");
914
}
915
916
@Override
917
protected void failed(long nanos, Throwable e, Description description) {
918
long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
919
System.out.println(description.getMethodName() + " failed in " + millis + "ms");
920
}
921
922
@Override
923
protected void finished(long nanos, Description description) {
924
long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
925
System.out.println(description.getMethodName() + " finished in " + millis + "ms");
926
}
927
};
928
929
@Test
930
public void performanceTest() {
931
// Timing is automatically recorded
932
performOperation();
933
}
934
}
935
936
// Logging slow tests
937
public class SlowTestLogger extends Stopwatch {
938
private static final long THRESHOLD_MS = 1000;
939
940
@Override
941
protected void finished(long nanos, Description description) {
942
long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
943
if (millis > THRESHOLD_MS) {
944
System.out.println("SLOW TEST: " + description.getMethodName()
945
+ " took " + millis + "ms");
946
}
947
}
948
}
949
```
950
951
### Verifier
952
953
Base class for rules that verify state after test execution. Similar to custom assertions.
954
955
```java { .api }
956
/**
957
* Base class for rules that verify conditions after test
958
*/
959
public abstract class Verifier implements TestRule {
960
/**
961
* Override to verify conditions after test
962
* Throw exception if verification fails
963
*/
964
protected void verify() throws Throwable;
965
}
966
```
967
968
**Usage Examples:**
969
970
```java
971
import org.junit.rules.Verifier;
972
import org.junit.Rule;
973
import org.junit.Test;
974
975
public class LogVerifier extends Verifier {
976
private Logger logger;
977
978
public LogVerifier(Logger logger) {
979
this.logger = logger;
980
}
981
982
@Override
983
protected void verify() {
984
if (logger.hasErrors()) {
985
throw new AssertionError("Test produced error logs: " + logger.getErrors());
986
}
987
}
988
}
989
990
public class VerifiedTest {
991
private Logger logger = new Logger();
992
993
@Rule
994
public Verifier logVerifier = new LogVerifier(logger);
995
996
@Test
997
public void testSomething() {
998
// Test code
999
// After test, verifier checks that no errors were logged
1000
}
1001
}
1002
1003
// State verification example
1004
public class StateVerifier extends Verifier {
1005
private SystemState state;
1006
1007
public StateVerifier(SystemState state) {
1008
this.state = state;
1009
}
1010
1011
@Override
1012
protected void verify() {
1013
if (!state.isClean()) {
1014
throw new AssertionError("System left in dirty state");
1015
}
1016
}
1017
}
1018
```
1019
1020
### DisableOnDebug
1021
1022
Wraps another rule and disables it when running in debug mode. Useful for timeout rules that interfere with debugging.
1023
1024
```java { .api }
1025
/**
1026
* Disables a rule when running in debug mode
1027
*/
1028
public class DisableOnDebug implements TestRule {
1029
/**
1030
* Create a rule that is disabled during debugging
1031
* @param rule - Rule to wrap
1032
*/
1033
public DisableOnDebug(TestRule rule);
1034
1035
/**
1036
* Check if currently debugging
1037
* @return true if debug mode detected
1038
*/
1039
public boolean isDebugging();
1040
}
1041
```
1042
1043
**Usage Examples:**
1044
1045
```java
1046
import org.junit.Rule;
1047
import org.junit.Test;
1048
import org.junit.rules.DisableOnDebug;
1049
import org.junit.rules.Timeout;
1050
1051
public class DebuggableTest {
1052
@Rule
1053
public TestRule timeout = new DisableOnDebug(Timeout.seconds(10));
1054
1055
@Test
1056
public void testWithTimeout() {
1057
// Timeout applies normally, but is disabled when debugging
1058
// so you can use breakpoints without timeout failures
1059
performOperation();
1060
}
1061
}
1062
```
1063
1064
## Types
1065
1066
```java { .api }
1067
/**
1068
* Represents a statement in test execution
1069
*/
1070
public abstract class Statement {
1071
/**
1072
* Execute the statement
1073
*/
1074
public abstract void evaluate() throws Throwable;
1075
}
1076
1077
/**
1078
* Legacy rule interface (prefer TestRule)
1079
*/
1080
@Deprecated
1081
public interface MethodRule {
1082
Statement apply(Statement base, FrameworkMethod method, Object target);
1083
}
1084
```
1085