0
# Standard Runners
1
2
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.
3
4
## Capabilities
5
6
### BlockJUnit4ClassRunner
7
8
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.
9
10
```java { .api }
11
/**
12
* Standard JUnit 4 test class runner
13
* This is the default runner used when no @RunWith annotation is present
14
*/
15
public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
16
/**
17
* Creates a runner for the given test class
18
* @param testClass - Class containing tests
19
* @throws InitializationError if the test class is malformed
20
*/
21
public BlockJUnit4ClassRunner(Class<?> testClass) throws InitializationError;
22
23
/**
24
* Returns the methods that run tests
25
* @return List of test methods
26
*/
27
protected List<FrameworkMethod> computeTestMethods();
28
29
/**
30
* Creates test instance
31
* @return New instance of test class
32
*/
33
protected Object createTest() throws Exception;
34
35
/**
36
* Validates that test class is correctly formed
37
* @param errors - Collects validation errors
38
*/
39
protected void collectInitializationErrors(List<Throwable> errors);
40
41
/**
42
* Returns Statement that executes test method
43
* @param method - Test method
44
* @return Statement for execution
45
*/
46
protected Statement methodBlock(FrameworkMethod method);
47
48
/**
49
* Returns Statement that invokes test method
50
* @param method - Test method
51
* @param test - Test instance
52
* @return Statement that invokes method
53
*/
54
protected Statement methodInvoker(FrameworkMethod method, Object test);
55
56
/**
57
* Returns rules that apply to the test
58
* @param target - Test instance
59
* @return List of TestRule objects
60
*/
61
protected List<TestRule> getTestRules(Object target);
62
63
/**
64
* Returns class rules
65
* @return List of TestRule objects for the class
66
*/
67
protected List<TestRule> classRules();
68
}
69
```
70
71
**Usage Examples:**
72
73
```java
74
import org.junit.runner.RunWith;
75
import org.junit.runners.BlockJUnit4ClassRunner;
76
import org.junit.Test;
77
import org.junit.Before;
78
import org.junit.After;
79
80
// Explicit use (normally not needed, this is the default)
81
@RunWith(BlockJUnit4ClassRunner.class)
82
public class StandardTest {
83
private Calculator calculator;
84
85
@Before
86
public void setUp() {
87
calculator = new Calculator();
88
}
89
90
@Test
91
public void testAddition() {
92
assertEquals(5, calculator.add(2, 3));
93
}
94
95
@Test
96
public void testSubtraction() {
97
assertEquals(1, calculator.subtract(3, 2));
98
}
99
100
@After
101
public void tearDown() {
102
calculator = null;
103
}
104
}
105
106
// Custom runner extending BlockJUnit4ClassRunner
107
public class TimingRunner extends BlockJUnit4ClassRunner {
108
public TimingRunner(Class<?> testClass) throws InitializationError {
109
super(testClass);
110
}
111
112
@Override
113
protected Statement methodInvoker(FrameworkMethod method, Object test) {
114
Statement statement = super.methodInvoker(method, test);
115
return new Statement() {
116
@Override
117
public void evaluate() throws Throwable {
118
long start = System.currentTimeMillis();
119
try {
120
statement.evaluate();
121
} finally {
122
long duration = System.currentTimeMillis() - start;
123
System.out.println(method.getName() + " took " + duration + "ms");
124
}
125
}
126
};
127
}
128
}
129
```
130
131
### Suite
132
133
Runs multiple test classes together as a suite. Useful for organizing tests and running related test classes as a group.
134
135
```java { .api }
136
/**
137
* Runner for test suites
138
* Executes multiple test classes together
139
*/
140
public class Suite extends ParentRunner<Runner> {
141
/**
142
* Creates a suite for the given class
143
* @param klass - Suite class annotated with @SuiteClasses
144
* @param builders - Runner builders
145
* @throws InitializationError if suite is malformed
146
*/
147
public Suite(Class<?> klass, RunnerBuilder builder) throws InitializationError;
148
149
/**
150
* Creates a suite with specific test classes
151
* @param klass - Suite class
152
* @param suiteClasses - Classes to include in suite
153
* @throws InitializationError if suite is malformed
154
*/
155
public Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError;
156
157
/**
158
* Creates a suite with specific runners
159
* @param klass - Suite class
160
* @param runners - Runners to include
161
* @throws InitializationError if suite is malformed
162
*/
163
public Suite(Class<?> klass, List<Runner> runners) throws InitializationError;
164
165
/**
166
* Creates an empty suite
167
* @return Empty suite runner
168
*/
169
public static Runner emptySuite();
170
171
/**
172
* Annotation for specifying classes in a suite
173
*/
174
@Retention(RetentionPolicy.RUNTIME)
175
@Target(ElementType.TYPE)
176
public @interface SuiteClasses {
177
Class<?>[] value();
178
}
179
}
180
```
181
182
**Usage Examples:**
183
184
```java
185
import org.junit.runner.RunWith;
186
import org.junit.runners.Suite;
187
import org.junit.runners.Suite.SuiteClasses;
188
189
// Basic suite
190
@RunWith(Suite.class)
191
@SuiteClasses({
192
TestClass1.class,
193
TestClass2.class,
194
TestClass3.class
195
})
196
public class AllTests {
197
// Empty class - just holds annotations
198
}
199
200
// Nested suites
201
@RunWith(Suite.class)
202
@SuiteClasses({
203
UnitTests.class,
204
IntegrationTests.class
205
})
206
public class AllProjectTests {
207
}
208
209
@RunWith(Suite.class)
210
@SuiteClasses({
211
CalculatorTest.class,
212
StringUtilsTest.class,
213
DateUtilsTest.class
214
})
215
public class UnitTests {
216
}
217
218
@RunWith(Suite.class)
219
@SuiteClasses({
220
DatabaseTest.class,
221
NetworkTest.class
222
})
223
public class IntegrationTests {
224
}
225
226
// Running the suite programmatically
227
import org.junit.runner.JUnitCore;
228
import org.junit.runner.Result;
229
230
public class TestRunner {
231
public static void main(String[] args) {
232
Result result = JUnitCore.runClasses(AllTests.class);
233
System.out.println("Tests run: " + result.getRunCount());
234
System.out.println("Failures: " + result.getFailureCount());
235
}
236
}
237
```
238
239
### Parameterized
240
241
Runs the same test multiple times with different parameters. Each parameter set creates a separate test instance.
242
243
```java { .api }
244
/**
245
* Runner for parameterized tests
246
* Runs tests multiple times with different parameter values
247
*/
248
public class Parameterized extends Suite {
249
/**
250
* Creates parameterized test runner
251
* @param klass - Test class
252
* @throws Throwable if initialization fails
253
*/
254
public Parameterized(Class<?> klass) throws Throwable;
255
256
/**
257
* Annotation for parameter data method
258
* Method must be static and return Collection<Object[]> or Iterable<Object[]>
259
*/
260
@Retention(RetentionPolicy.RUNTIME)
261
@Target(ElementType.METHOD)
262
public @interface Parameters {
263
/**
264
* Optional name pattern for test iterations
265
* Use {index} for iteration number, {0}, {1}, etc. for parameter values
266
*/
267
String name() default "{index}";
268
}
269
270
/**
271
* Annotation for parameter field (not constructor parameter)
272
* Indicates which parameter from the array should be injected
273
*/
274
@Retention(RetentionPolicy.RUNTIME)
275
@Target(ElementType.FIELD)
276
public @interface Parameter {
277
/**
278
* Index in parameter array (default 0)
279
*/
280
int value() default 0;
281
}
282
283
/**
284
* Annotation for methods that run before each parameter set
285
*/
286
@Retention(RetentionPolicy.RUNTIME)
287
@Target(ElementType.METHOD)
288
public @interface BeforeParam {}
289
290
/**
291
* Annotation for methods that run after each parameter set
292
*/
293
@Retention(RetentionPolicy.RUNTIME)
294
@Target(ElementType.METHOD)
295
public @interface AfterParam {}
296
297
/**
298
* Use custom factory for creating test runners
299
*/
300
@Retention(RetentionPolicy.RUNTIME)
301
@Target(ElementType.TYPE)
302
public @interface UseParametersRunnerFactory {
303
Class<? extends ParametersRunnerFactory> value();
304
}
305
}
306
```
307
308
**Usage Examples:**
309
310
```java
311
import org.junit.runner.RunWith;
312
import org.junit.runners.Parameterized;
313
import org.junit.runners.Parameterized.Parameter;
314
import org.junit.runners.Parameterized.Parameters;
315
import org.junit.Test;
316
import static org.junit.Assert.*;
317
import java.util.Arrays;
318
import java.util.Collection;
319
320
// Field injection style
321
@RunWith(Parameterized.class)
322
public class FibonacciTest {
323
@Parameter(0)
324
public int input;
325
326
@Parameter(1)
327
public int expected;
328
329
@Parameters(name = "fib({0}) = {1}")
330
public static Collection<Object[]> data() {
331
return Arrays.asList(new Object[][] {
332
{0, 0},
333
{1, 1},
334
{2, 1},
335
{3, 2},
336
{4, 3},
337
{5, 5},
338
{6, 8}
339
});
340
}
341
342
@Test
343
public void testFibonacci() {
344
assertEquals(expected, Fibonacci.compute(input));
345
}
346
}
347
348
// Constructor injection style
349
@RunWith(Parameterized.class)
350
public class AdditionTest {
351
private int a;
352
private int b;
353
private int sum;
354
355
public AdditionTest(int a, int b, int sum) {
356
this.a = a;
357
this.b = b;
358
this.sum = sum;
359
}
360
361
@Parameters(name = "{0} + {1} = {2}")
362
public static Collection<Object[]> data() {
363
return Arrays.asList(new Object[][] {
364
{1, 1, 2},
365
{2, 3, 5},
366
{5, 7, 12},
367
{10, 20, 30}
368
});
369
}
370
371
@Test
372
public void testAddition() {
373
assertEquals(sum, a + b);
374
}
375
}
376
377
// Using Iterable instead of Collection
378
@RunWith(Parameterized.class)
379
public class PrimeTest {
380
@Parameter
381
public int number;
382
383
@Parameters(name = "isPrime({0})")
384
public static Iterable<Object[]> data() {
385
return Arrays.asList(new Object[][] {
386
{2}, {3}, {5}, {7}, {11}, {13}
387
});
388
}
389
390
@Test
391
public void testPrime() {
392
assertTrue("Should be prime: " + number, isPrime(number));
393
}
394
}
395
396
// Complex parameter objects
397
@RunWith(Parameterized.class)
398
public class UserValidationTest {
399
@Parameter(0)
400
public User user;
401
402
@Parameter(1)
403
public boolean expectedValid;
404
405
@Parameters(name = "{0} is valid: {1}")
406
public static Collection<Object[]> data() {
407
return Arrays.asList(new Object[][] {
408
{new User("Alice", 25), true},
409
{new User("Bob", -1), false},
410
{new User("", 30), false},
411
{new User(null, 20), false}
412
});
413
}
414
415
@Test
416
public void testValidation() {
417
assertEquals(expectedValid, user.isValid());
418
}
419
}
420
```
421
422
### JUnit4
423
424
Explicit JUnit 4 runner. Functionally identical to `BlockJUnit4ClassRunner` but with a simpler name.
425
426
```java { .api }
427
/**
428
* Alias for BlockJUnit4ClassRunner with a shorter name
429
*/
430
public class JUnit4 extends BlockJUnit4ClassRunner {
431
/**
432
* Creates a JUnit 4 runner
433
* @param klass - Test class
434
* @throws InitializationError if test class is malformed
435
*/
436
public JUnit4(Class<?> klass) throws InitializationError;
437
}
438
```
439
440
**Usage Examples:**
441
442
```java
443
import org.junit.runner.RunWith;
444
import org.junit.runners.JUnit4;
445
import org.junit.Test;
446
447
@RunWith(JUnit4.class)
448
public class SimpleTest {
449
@Test
450
public void testSomething() {
451
assertTrue(true);
452
}
453
}
454
```
455
456
### AllTests
457
458
Runner for JUnit 3.x compatibility. Allows running legacy JUnit 3 test suites.
459
460
```java { .api }
461
/**
462
* Runner for JUnit 3.x style test suites
463
* Provides backward compatibility with JUnit 3
464
*/
465
public class AllTests extends SuiteMethod {
466
/**
467
* Creates runner for JUnit 3 style suite
468
* @param klass - Class with static suite() method
469
* @throws Throwable if initialization fails
470
*/
471
public AllTests(Class<?> klass) throws Throwable;
472
}
473
```
474
475
**Usage Examples:**
476
477
```java
478
import org.junit.runner.RunWith;
479
import org.junit.runners.AllTests;
480
import junit.framework.Test;
481
import junit.framework.TestSuite;
482
483
@RunWith(AllTests.class)
484
public class LegacyTests {
485
public static Test suite() {
486
TestSuite suite = new TestSuite("Legacy Test Suite");
487
suite.addTestSuite(OldStyleTest1.class);
488
suite.addTestSuite(OldStyleTest2.class);
489
return suite;
490
}
491
}
492
```
493
494
### Enclosed
495
496
Runs all inner classes of a test class as separate test classes. Useful for organizing related tests in nested classes.
497
498
```java { .api }
499
/**
500
* Runner that executes all inner classes as tests
501
* Each inner class is run with its own runner
502
*/
503
public class Enclosed extends Suite {
504
/**
505
* Creates enclosed runner
506
* @param klass - Outer test class
507
* @param builder - Runner builder
508
* @throws Throwable if initialization fails
509
*/
510
public Enclosed(Class<?> klass, RunnerBuilder builder) throws Throwable;
511
}
512
```
513
514
**Usage Examples:**
515
516
```java
517
import org.junit.experimental.runners.Enclosed;
518
import org.junit.runner.RunWith;
519
import org.junit.Test;
520
import static org.junit.Assert.*;
521
522
@RunWith(Enclosed.class)
523
public class CalculatorTests {
524
// Each inner class runs as separate test
525
public static class AdditionTests {
526
@Test
527
public void testPositiveNumbers() {
528
assertEquals(5, 2 + 3);
529
}
530
531
@Test
532
public void testNegativeNumbers() {
533
assertEquals(-5, -2 + -3);
534
}
535
}
536
537
public static class SubtractionTests {
538
@Test
539
public void testPositiveNumbers() {
540
assertEquals(1, 3 - 2);
541
}
542
543
@Test
544
public void testNegativeNumbers() {
545
assertEquals(-1, -3 - -2);
546
}
547
}
548
549
public static class MultiplicationTests {
550
@Test
551
public void testPositiveNumbers() {
552
assertEquals(6, 2 * 3);
553
}
554
}
555
}
556
```
557
558
## Types
559
560
```java { .api }
561
/**
562
* Base class for runners that have children
563
*/
564
public abstract class ParentRunner<T> extends Runner {
565
protected ParentRunner(Class<?> testClass) throws InitializationError;
566
567
/**
568
* Get children to run
569
* @return List of children
570
*/
571
protected abstract List<T> getChildren();
572
573
/**
574
* Get description for a child
575
* @param child - Child to describe
576
* @return Description
577
*/
578
protected abstract Description describeChild(T child);
579
580
/**
581
* Run a child
582
* @param child - Child to run
583
* @param notifier - Notifier for events
584
*/
585
protected abstract void runChild(T child, RunNotifier notifier);
586
587
/**
588
* Returns Statement for running all children
589
* @param notifier - Notifier for events
590
* @return Statement
591
*/
592
protected Statement childrenInvoker(RunNotifier notifier);
593
}
594
595
/**
596
* Factory for creating test runners
597
*/
598
public interface ParametersRunnerFactory {
599
/**
600
* Create runner for parameterized test
601
* @param testWithParameters - Test parameters
602
* @return Runner for the test
603
*/
604
Runner createRunnerForTestWithParameters(TestWithParameters testWithParameters) throws InitializationError;
605
}
606
607
/**
608
* Default factory for creating parameterized test runners
609
*/
610
public class BlockJUnit4ClassRunnerWithParametersFactory implements ParametersRunnerFactory {
611
@Override
612
public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError;
613
}
614
615
/**
616
* BlockJUnit4ClassRunner for a single parameter set
617
*/
618
public class BlockJUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunner {
619
public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test) throws InitializationError;
620
}
621
622
/**
623
* Container for parameterized test data
624
*/
625
public class TestWithParameters {
626
public TestWithParameters(String name, TestClass testClass, List<Object> parameters);
627
628
public String getName();
629
public TestClass getTestClass();
630
public List<Object> getParameters();
631
}
632
633
/**
634
* Wraps a test class with metadata
635
*/
636
public class TestClass {
637
/**
638
* Creates a TestClass wrapping the given class
639
* Scans the class for annotations (expensive operation - share instances when possible)
640
* @param clazz - The class to wrap
641
* @throws IllegalArgumentException if class has more than one constructor
642
*/
643
public TestClass(Class<?> clazz);
644
645
/**
646
* Returns the underlying Java class
647
* @return The wrapped class
648
*/
649
public Class<?> getJavaClass();
650
651
/**
652
* Returns the class's name
653
* @return The class name
654
*/
655
public String getName();
656
657
/**
658
* Returns the only public constructor in the class
659
* @return The single public constructor
660
* @throws AssertionError if there are more or less than one
661
*/
662
public Constructor<?> getOnlyConstructor();
663
664
/**
665
* Returns all non-overridden methods that are annotated
666
* @return List of all annotated methods in the class hierarchy
667
* @since 4.12
668
*/
669
public List<FrameworkMethod> getAnnotatedMethods();
670
671
/**
672
* Returns all non-overridden methods annotated with the specified annotation
673
* @param annotationClass - The annotation class to search for
674
* @return List of methods with the annotation
675
*/
676
public List<FrameworkMethod> getAnnotatedMethods(Class<? extends Annotation> annotationClass);
677
678
/**
679
* Returns all non-overridden fields that are annotated
680
* @return List of all annotated fields in the class hierarchy
681
* @since 4.12
682
*/
683
public List<FrameworkField> getAnnotatedFields();
684
685
/**
686
* Returns all non-overridden fields annotated with the specified annotation
687
* @param annotationClass - The annotation class to search for
688
* @return List of fields with the annotation
689
*/
690
public List<FrameworkField> getAnnotatedFields(Class<? extends Annotation> annotationClass);
691
692
/**
693
* Gets values of annotated fields on the test instance
694
* @param test - Test instance
695
* @param annotationClass - Annotation to search for
696
* @param valueClass - Expected type of field values
697
* @return List of field values
698
*/
699
public <T> List<T> getAnnotatedFieldValues(Object test,
700
Class<? extends Annotation> annotationClass, Class<T> valueClass);
701
702
/**
703
* Finds annotated fields with the specified type, retrieves values and passes to consumer
704
* @param test - Test instance
705
* @param annotationClass - Annotation to search for
706
* @param valueClass - Expected type of field values
707
* @param consumer - Consumer to receive field values
708
* @since 4.13
709
*/
710
public <T> void collectAnnotatedFieldValues(Object test,
711
Class<? extends Annotation> annotationClass, Class<T> valueClass,
712
MemberValueConsumer<T> consumer);
713
714
/**
715
* Gets return values of annotated methods on the test instance
716
* @param test - Test instance
717
* @param annotationClass - Annotation to search for
718
* @param valueClass - Expected return type
719
* @return List of method return values
720
*/
721
public <T> List<T> getAnnotatedMethodValues(Object test,
722
Class<? extends Annotation> annotationClass, Class<T> valueClass);
723
724
/**
725
* Finds annotated methods with the specified return type, invokes them and passes results to consumer
726
* @param test - Test instance
727
* @param annotationClass - Annotation to search for
728
* @param valueClass - Expected return type
729
* @param consumer - Consumer to receive method return values
730
* @since 4.13
731
*/
732
public <T> void collectAnnotatedMethodValues(Object test,
733
Class<? extends Annotation> annotationClass, Class<T> valueClass,
734
MemberValueConsumer<T> consumer);
735
736
/**
737
* Returns whether the class is public
738
* @return true if the class has public modifier
739
*/
740
public boolean isPublic();
741
742
/**
743
* Returns whether the class is a non-static inner class
744
* @return true if the class is a member class and not static
745
*/
746
public boolean isANonStaticInnerClass();
747
748
/**
749
* Returns the annotations on this class
750
* @return Array of annotations
751
*/
752
public Annotation[] getAnnotations();
753
754
/**
755
* Returns the annotation of the specified type on this class
756
* @param annotationClass - Annotation type to search for
757
* @return The annotation, or null if not present
758
*/
759
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
760
}
761
762
/**
763
* Consumer interface for collecting annotated member values
764
* @since 4.13
765
*/
766
public interface MemberValueConsumer<T> {
767
/**
768
* Accepts a member and its value
769
* @param member - The framework member (field or method)
770
* @param value - The value from the member
771
*/
772
void accept(FrameworkMember<?> member, T value);
773
}
774
775
/**
776
* Represents a method on a test class
777
*/
778
public class FrameworkMethod {
779
/**
780
* Creates a FrameworkMethod for the given method
781
* @param method - The method to wrap
782
* @throws NullPointerException if method is null
783
*/
784
public FrameworkMethod(Method method);
785
786
/**
787
* Returns the underlying Java method
788
* @return The wrapped method
789
*/
790
public Method getMethod();
791
792
/**
793
* Returns the method's name
794
* @return The method name
795
*/
796
public String getName();
797
798
/**
799
* Invokes this method on target with parameters
800
* InvocationTargetExceptions are unwrapped and their causes rethrown
801
* @param target - Object to invoke method on
802
* @param params - Method parameters
803
* @return The method's return value
804
* @throws Throwable if the method throws an exception
805
*/
806
public Object invokeExplosively(Object target, Object... params) throws Throwable;
807
808
/**
809
* Returns the return type of the method
810
* @return The method's return type
811
*/
812
public Class<?> getReturnType();
813
814
/**
815
* Returns the return type of the method (alias for getReturnType)
816
* @return The method's return type
817
*/
818
public Class<?> getType();
819
820
/**
821
* Returns the class where the method is actually declared
822
* @return The declaring class
823
*/
824
public Class<?> getDeclaringClass();
825
826
/**
827
* Validates that the method is public, void, has no arguments
828
* Adds errors to the list if validation fails
829
* @param isStatic - Whether method should be static
830
* @param errors - List to collect validation errors
831
*/
832
public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors);
833
834
/**
835
* Validates that the method is public, void
836
* Adds errors to the list if validation fails
837
* @param isStatic - Whether method should be static
838
* @param errors - List to collect validation errors
839
*/
840
public void validatePublicVoid(boolean isStatic, List<Throwable> errors);
841
842
/**
843
* Validates that method arguments have no type parameters
844
* Adds errors to the list if validation fails
845
* @param errors - List to collect validation errors
846
*/
847
public void validateNoTypeParametersOnArgs(List<Throwable> errors);
848
849
/**
850
* Returns the annotations on this method
851
* @return Array of annotations
852
*/
853
public Annotation[] getAnnotations();
854
855
/**
856
* Returns the annotation of the specified type on this method
857
* @param annotationClass - Annotation type to search for
858
* @return The annotation, or null if not present
859
*/
860
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
861
862
/**
863
* Returns true if this is a no-arg method that returns a value assignable to type
864
* @param type - The type to check
865
* @return true if method produces the specified type
866
* @deprecated Used only by Theories runner, will be replaced
867
*/
868
@Deprecated
869
public boolean producesType(Type type);
870
}
871
872
/**
873
* Represents a field on a test class
874
*/
875
public class FrameworkField {
876
public FrameworkField(Field field);
877
878
public Field getField();
879
public String getName();
880
public Object get(Object target) throws IllegalAccessException;
881
public Class<?> getType();
882
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
883
}
884
885
/**
886
* Exception thrown during runner initialization
887
*/
888
public class InitializationError extends Exception {
889
public InitializationError(List<Throwable> errors);
890
public InitializationError(Throwable error);
891
public InitializationError(String message);
892
893
public List<Throwable> getCauses();
894
}
895
```
896