As of Android 24 (N), JUnit3 style javatests have been deprecated for the new JUnit4-based Android Testing Support Library. We are in the process of changing all instrumentation tests in chromium to JUnit4 style. This doc explains the differences between JUnit3 and JUnit4 instrumentation tests and how to write or convert them.
JUnit3 | JUnit4 | |
---|---|---|
Inheritance | Tests extends TestCase or child classes | No inheritance. |
Test methods | methods named with test prefix | methods annotated with @Test |
Set up | setUp() method | public method annotated with @Before |
Tear down | tearDown() method | public method annotated with @After |
Test runner | declared within test apk AndroidManifest.xml | Must specify chromium-junit4:"true" |
Class runner | N/A | @RunWith(XClassRunner.class) |
Assertion | Extends from junit.framework.Assert, inherited APIs | Use static methods from org.junit.Assert |
Please note that during the migration, we support running JUnit3 and JUnit4 tests in the same apk. This requires two tags, one each for JUnit3 and JUnit4. The tag for the JUnit4 runner must specify
chromium-junit4:"true"
(Example)
@Test(expected=MyException.class)
. Tests annotated this way will fail if they do not throw the given exception.@BeforeClass
@AfterClass
RuleChain
ActivityTestRule
is a special TestRule
provided by Android Testing Support Library that allows tests to launch an Activity. (Documentation)JUnit3:
public class MyTestClass extends MyActivityInstrumentationTestCase2<TestActivity> { @Override protected void setUp(){ super.setUp(); setActivityIntent(new Intent()); getActivity(); } @Override protected void tearDown() { specialActionFromSuper(); super.tearDown(); } public void testA() { assertEquals(1, 1); } }
JUnit4:
@RunWith(BaseJUnit4ClassRunner.class); public class TestClass { @Rule public ActivityTestRule<TestActivity> mRule = new ActivityTestRule<>(TestActivity.class); @Before public void setUp() { //Must be public mRule.launchActivity(new Intent()); } @After public void tearDown() { //Must be public mRule.specialActionFromActivityTestRule(); } @Test public void testA() { Assert.assertEquals(1, 1); } }
//third_party/junit
//third_party/android_support_test_runner:runner_java
(for AndroidJUnitRunner
, etc)//third_party/android_support_test_runner:rules_java
(for ActivityTestRule
, etc)TestRule:
public class MyRule implements TestRule { // 1: Add utility methods... @Override public Statement apply(final Statement base, Description desc) { return new Statement() { @Override public void evaluate() { // 2: Code here runs before @Before method base.evaluate() // 3: Code here runs after @After method } } } }
In our Junit3 tests command line flags (set by the CommandLineFlag annotations) were inherited from the test base classes. As an example, ChromeActivityTestBase is annotated with:
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, ...
and as a result any test in a class derived from ChromeActivityTestBase will disable the first run experience.
The Junit4 tests classes are not however, derived from test base classes; instead their behavior is defined by test rules. To support this our Junit4 test runner will examine the command line flag annotations on all rules referenced with @Rule annotations in the test class. In addition, where one rule is derived from another, the command line flags propogate through the hierarchy of rules. See, for example, BottomSheetTestRule
Note:- This has only recently been implemented, so is not yet used in all tests. See this bug
The CommandLineFlags annonations are more fully documented in the CommandLineFlags class
Instrumentation tests that rely on test thread to have message handler will not work. For example error message:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
or
java.lang.IllegalStateException: The current thread must have a looper!
Please utilize ActivityTestRule.runOnUiThread(Runnable r)
to refactor these tests. For more, check this GitHub issue
Use @UiThreadTest
with caution!!
android.support.test.annotation.UiThreadTest
, NOT android.test.UiThreadTest
.setUp
and tearDown
to run in Ui Thread as well. Avoid that by calling runOnUiThread
or runOnMainSync
with a Runnable.// Wrong test public class Test { @Rule public ActivityTestRule<MyActivity> mRule = new ActivityTestRule<>( MyActivity.class> @Before public void setUp() { // Cause failure because this also runs on Ui Thread, while it // is intended for Instrumentation worker thread mRule.launchActivity() } @UiThreadTest public void test() { actionThatNeedsUiThread(); } }
The correct thing to do is
// Correct test public class Test { @Rule public ActivityTestRule<MyActivity> mRule = new ActivityTestRule<>( MyActivity.class> @Before public void setUp() { mRule.launchActivity() } public void test() { mRule.runOnUiThread(new Runnable() { @Override public void run() { actionThatNeedsUiThread(); } }); } }
assertEquals(float a, float b)
and assertEquals(double a, double b)
are deprecated in JUnit4's Assert class. Despite only generating a warning at build time, they fail at runtime. Please use Assert.assertEquals(float a, float b, float delta)
Errorprone expects all public methods starting with test...
to be annotated with @Test
. Failure to meet that expectation will cause errorprone to fail with something like this:
[JUnit4TestNotRun] Test method will not be run; please add @Test annotation
In particular, you may see this when attempting to disable tests. In that case, the test should be annotated with both @DisabledTest and @Test.
@Test
and @LargeTest/@MediumTest/@SmallTest
annotation both necessary?If you have any other questions, feel free to report in this bug.