1# JUnit 4 Migration
2
3As of Android 24 (N), JUnit3 style javatests have been deprecated for the new
4JUnit4-based [Android Testing Support Library][1].
5We are in the process of changing all instrumentation tests in chromium to
6JUnit4 style. This doc explains the differences between JUnit3 and JUnit4
7instrumentation tests and how to write or convert them.
8
9[TOC]
10
11## Differences between JUnit3 and JUnit4 instrumentation tests
12
13|              | JUnit3                                              | JUnit4                                   |
14|--------------|-----------------------------------------------------|------------------------------------------|
15| Inheritance  | Tests extends TestCase or child classes             | No inheritance.                          |
16| Test methods | methods named with test prefix                      | methods annotated with @Test             |
17| Set up       | setUp() method                                      | public method annotated with @Before     |
18| Tear down    | tearDown() method                                   | public method annotated with @After      |
19| Test runner  | declared within test apk AndroidManifest.xml        | Must specify `chromium-junit4:"true"`    |
20| Class runner | N/A                                                 | @RunWith(XClassRunner.class)             |
21| Assertion    | Extends from junit.framework.Assert, inherited APIs | Use static methods from org.junit.Assert |
22
23> Please note that during the migration, we support running JUnit3 and JUnit4
24> tests in the same apk. This requires two tags, one each for JUnit3 and JUnit4.
25> The tag for the JUnit4 runner must specify `chromium-junit4:"true"`
26> ([Example][2])
27
28-   **Other JUnit4 features**:
29    - Tests can be annotated to expect an exception, e.g.
30      `@Test(expected=MyException.class)`. Tests annotated this way will
31      fail if they do not throw the given exception.
32   - **Test suite set up**: public static method annotated with `@BeforeClass`
33   - **Test suite tear down**: public static method annotated with
34                                      `@AfterClass`
35-  **Replacement for JUnit3 test base classes**
36    - [`TestRule`][3]:
37        - TestRule is a class to **outsource your test setUp, tearDown, and
38          utility methods**. Since there are no more interitance and TestBase classes,
39          one should use TestRule for any API calls provided by its test base classes
40          previously.
41        - One test can declare multiple TestRules and the class runner will run all of
42          them. If the order of the TestRule matters to you, use
43          [`RuleChain`][8]
44
45    - [`ActivityTestRule`][4]
46        - `ActivityTestRule` is a special `TestRule` provided by Android Testing
47          Support Library that allows tests to launch an Activity.
48          ([Documentation][4])
49
50## Example Code of JUnit3 test and JUnit4 test
51
52JUnit3:
53
54```java
55public class MyTestClass extends MyActivityInstrumentationTestCase2<TestActivity> {
56    @Override
57    protected void setUp(){
58        super.setUp();
59        setActivityIntent(new Intent());
60        getActivity();
61    }
62
63    @Override
64    protected void tearDown() {
65        specialActionFromSuper();
66        super.tearDown();
67    }
68
69    public void testA() {
70        assertEquals(1, 1);
71    }
72}
73```
74
75JUnit4:
76
77```java
78@RunWith(BaseJUnit4ClassRunner.class);
79public class TestClass {
80    @Rule public ActivityTestRule<TestActivity> mRule = new ActivityTestRule<>(TestActivity.class);
81
82    @Before
83    public void setUp() { //Must be public
84        mRule.launchActivity(new Intent());
85    }
86
87    @After
88    public void tearDown() { //Must be public
89        mRule.specialActionFromActivityTestRule();
90    }
91
92    @Test
93    public void testA() {
94        Assert.assertEquals(1, 1);
95    }
96}
97```
98
99## Migration process
100
1011.  Add required libraries to your target dependencies in BUILD.gn
102    -   JUnit 4 library: `//third_party/junit`
103    -   Android Testing Support Rules:
104        -   `//third_party/android_support_test_runner:runner_java`
105            (for `AndroidJUnitRunner`, etc)
106        -   `//third_party/android_support_test_runner:rules_java`
107            (for `ActivityTestRule`, etc)
1081.  Add class runner to your test apk manifest.
109    ([example][2])
110    -   Keep in mind you can have multiple instrumentations in your manifest.
111        Our test runner will run JUnit4 tests with JUnit4 runner and JUnit3
112        tests with non-JUnit4 runner.
1131.  Refactor TestBase class to a TestRule class.
114    ([example CL](https://codereview.chromium.org/2632043002))
115    -   +yolandyan will do this part, however, if you did refactoring yourself,
116        please add him as a reviewer for your CL and enjoy his eternal appreciation!
1171.  Use [auto migrate script][5] to or manually convert all JUnit3 tests to
118    JUnit4 style in a your javatest directory
119    -   we understand it's tedious to just manually write all the annotations,
120        change modifiers, etc to convert all the javatest, so we created an auto
121        change script that helps you to convert all the javatests in a certain
122        directory. Please check its [README page][5]
123        on instructions.
124
125
126## Customized TestRule example
127
128TestRule:
129
130```java
131public class MyRule implements TestRule {
132    // 1: Add utility methods...
133
134    @Override
135    public Statement apply(final Statement base, Description desc) {
136        return new Statement() {
137            @Override
138            public void evaluate() {
139                // 2: Code here runs before @Before method
140                base.evaluate()
141                // 3: Code here runs after @After method
142            }
143        }
144    }
145}
146```
147
148## Command Line Flags
149
150In our Junit3 tests command line flags (set by the CommandLineFlag annotations) were inherited from the
151test base classes. As an example, ChromeActivityTestBase is annotated with:
152
153```java
154@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, ...
155```
156
157and as a result any test in a class derived from ChromeActivityTestBase will disable the first run experience.
158
159The Junit4 tests classes are not however, derived from test base classes; instead their behavior is defined by
160test rules. To support this our Junit4 test runner will examine the command line flag annotations on all rules
161referenced with @Rule annotations in the test class. In addition, where one rule is derived from another, the
162command line flags propogate through the hierarchy of rules. See, for example, [BottomSheetTestRule][11]
163
164Note:- This has only recently been implemented, so is not yet used in all tests. See [this bug][12]
165
166The CommandLineFlags annonations are more fully documented in the [CommandLineFlags class][13]
167
168## Common Errors
169
1701.  Instrumentation tests that rely on test thread to have message handler
171    will not work. For example error message:
172
173        java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
174
175    or
176
177        java.lang.IllegalStateException: The current thread must have a looper!
178
179    Please utilize `ActivityTestRule.runOnUiThread(Runnable r)` to refactor
180    these tests. For more, check this [GitHub issue][6]
181
1821.  Use `@UiThreadTest` with caution!!
183    -   Currently, **@UiThreadTest is only effective when UiThreadTestRule or
184        ActivityTestRule is declared** in the test class.
185    -   Please use **`android.support.test.annotation.UiThreadTest`, NOT
186        `android.test.UiThreadTest`**.
187    -   When using @UiThreadTest, **it would cause `setUp` and `tearDown` to
188        run in Ui Thread** as well. Avoid that by calling [`runOnUiThread`][9]
189        or [`runOnMainSync`][10] with a Runnable.
190
191    ```java
192    // Wrong test
193    public class Test {
194        @Rule
195        public ActivityTestRule<MyActivity> mRule = new ActivityTestRule<>(
196                MyActivity.class);
197
198        @Before
199        public void setUp() {
200            // Cause failure because this also runs on Ui Thread, while it
201            // is intended for Instrumentation worker thread
202            mRule.launchActivity();
203        }
204
205        @UiThreadTest
206        public void test() {
207            actionThatNeedsUiThread();
208        }
209    }
210    ```
211
212    The correct thing to do is
213
214    ```java
215    // Correct test
216    public class Test {
217        @Rule
218        public ActivityTestRule<MyActivity> mRule = new ActivityTestRule<>(
219                MyActivity.class);
220
221        @Before
222        public void setUp() {
223            mRule.launchActivity();
224        }
225
226        public void test() {
227            mRule.runOnUiThread(new Runnable() {
228                @Override
229                public void run() {
230                    actionThatNeedsUiThread();
231                }
232            });
233        }
234    }
235    ```
236
2371.  `assertEquals(float a, float b)` and `assertEquals(double a, double b)` are
238    deprecated in JUnit4's Assert class. **Despite only generating a warning at
239    build time, they fail at runtime.** Please use
240    `Assert.assertEquals(float a, float b, float delta)`
241
2421.  Errorprone expects all public methods starting with `test...` to be
243    annotated with `@Test`. Failure to meet that expectation will cause
244    errorprone to fail with something like this:
245
246        [JUnit4TestNotRun] Test method will not be run; please add @Test annotation
247
248    In particular, you may see this when attempting to disable tests. In that
249    case, the test should be annotated with both @DisabledTest and @Test.
250
251## Common questions
252
253-   Q: Are `@Test` and `@LargeTest/@MediumTest/@SmallTest` annotation
254    both necessary?
255    -   A: Yes, both are required for now. We plan to refactor this in the
256        future.
257-   Q: Isn't the inheritance of the Test classes just migrated to inheritance
258    of TestRules?
259    -   A: Yes. During the migration, we plan to maintain a 1:1 mapping between
260        the test base classes and TestRules (e.g. ContentShellTestBase to
261        ContentShellTestRule in this
262        [CL](https://codereview.chromium.org/2632043002)).
263        This allows the auto convert script to replace API calls in any
264        JUnit3 tests. After the migration, we plan to refactor the TestRules to
265        be more modular.
266
267If you have any other questions, feel free to report in [this bug][7].
268
269## Links and Crbugs
270
271- [Android Test Support Library documentation][1]
272- [Auto change script][5]
273- [Crbug for JUnit3 to JUnit4 migration][7]
274
275[1]: https://developer.android.com/topic/libraries/testing-support-library/index.html
276[2]: https://cs.chromium.org/chromium/src/android_webview/tools/system_webview_shell/layout_tests/AndroidManifest.xml?l=36
277[3]: http://junit.org/junit4/javadoc/4.12/org/junit/rules/TestRule.html
278[4]: https://developer.android.com/reference/android/support/test/rule/ActivityTestRule.html
279[5]: https://github.com/yoland68/chromium-junit-auto-migrate
280[6]: http://github.com/skyisle/android-test-kit/issues/121
281[7]: https://bugs.chromium.org/p/chromium/issues/detail?id=640116
282[8]: http://junit.org/junit4/javadoc/4.12/org/junit/rules/RuleChain.html
283[9]: https://developer.android.com/reference/android/app/Instrumentation.html#runOnMainSync(java.lang.Runnable)
284[10]: https://developer.android.com/reference/android/support/test/rule/UiThreadTestRule.html#runOnUiThread(java.lang.Runnable)
285[11]: /chrome/test/android/javatests/src/org/chromium/chrome/test/BottomSheetTestRule.java
286[12]: https://bugs.chromium.org/p/chromium/issues/detail?id=734553
287[13]: /base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
288