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